summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-09-21 16:38:09 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-09-21 16:38:09 -0700
commit95a0c7c2d6cfde3fb5fdb713428ed0df4d6bdd58 (patch)
treeeed1998bf14a6193c3f1ccd9b2b638fba6f69088 /drivers/media
parent1dbc080c9ef6bcfba652ef0d6ae919b8c7c85a1d (diff)
parenta205425658dead19bb1b8ac00584aed98e60dde2 (diff)
Merge tag 'ib-mfd-many-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into next
Merge "Immutable branch between MFD and many other subsystems due for the v4.14 merge window" to get the TWL headers moved to the right place.
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/cec/cec-adap.c88
-rw-r--r--drivers/media/cec/cec-api.c5
-rw-r--r--drivers/media/cec/cec-core.c1
-rw-r--r--drivers/media/cec/cec-notifier.c6
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c182
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.h7
-rw-r--r--drivers/media/dvb-core/dvb_net.c5
-rw-r--r--drivers/media/dvb-frontends/Kconfig1
-rw-r--r--drivers/media/dvb-frontends/af9013.c1186
-rw-r--r--drivers/media/dvb-frontends/af9013.h86
-rw-r--r--drivers/media/dvb-frontends/af9013_priv.h2
-rw-r--r--drivers/media/dvb-frontends/au8522_common.c1
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c74
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c215
-rw-r--r--drivers/media/dvb-frontends/bcm3510.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c307
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.h10
-rw-r--r--drivers/media/dvb-frontends/cxd2841er_priv.h3
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c6
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h15
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c20
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c10
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c20
-rw-r--r--drivers/media/dvb-frontends/lnbh25.c6
-rw-r--r--drivers/media/dvb-frontends/mt352.c1
-rw-r--r--drivers/media/dvb-frontends/or51132.c4
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c4
-rw-r--r--drivers/media/dvb-frontends/stv0367.c1354
-rw-r--r--drivers/media/dvb-frontends/stv0367.h13
-rw-r--r--drivers/media/dvb-frontends/stv0367_defs.h1301
-rw-r--r--drivers/media/dvb-frontends/stv0367_regs.h4
-rw-r--r--drivers/media/dvb-frontends/zl10353.c3
-rw-r--r--drivers/media/i2c/Kconfig51
-rw-r--r--drivers/media/i2c/Makefile5
-rw-r--r--drivers/media/i2c/ad5820.c2
-rw-r--r--drivers/media/i2c/adv7180.c2
-rw-r--r--drivers/media/i2c/adv7604.c7
-rw-r--r--drivers/media/i2c/as3645a.c12
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c36
-rw-r--r--drivers/media/i2c/dw9714.c291
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c1
-rw-r--r--drivers/media/i2c/max2175.c1453
-rw-r--r--drivers/media/i2c/max2175.h109
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c1
-rw-r--r--drivers/media/i2c/mt9v032.c7
-rw-r--r--drivers/media/i2c/ov13858.c1816
-rw-r--r--drivers/media/i2c/ov2659.c11
-rw-r--r--drivers/media/i2c/ov5640.c2344
-rw-r--r--drivers/media/i2c/ov5645.c7
-rw-r--r--drivers/media/i2c/ov5647.c7
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c7
-rw-r--r--drivers/media/i2c/s5k5baf.c6
-rw-r--r--drivers/media/i2c/s5k6aa.c2
-rw-r--r--drivers/media/i2c/smiapp/Kconfig1
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c29
-rw-r--r--drivers/media/i2c/soc_camera/ov6650.c2
-rw-r--r--drivers/media/i2c/soc_camera/ov772x.c6
-rw-r--r--drivers/media/i2c/tc358743.c77
-rw-r--r--drivers/media/i2c/tvp514x.c6
-rw-r--r--drivers/media/i2c/tvp5150.c32
-rw-r--r--drivers/media/i2c/tvp7002.c6
-rw-r--r--drivers/media/media-entity.c43
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c1
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c2
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.c4
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c3
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c9
-rw-r--r--drivers/media/pci/cx88/cx88-video.c4
-rw-r--r--drivers/media/pci/ddbridge/Kconfig6
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c613
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-regs.h4
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h41
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c4
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_core.c3
-rw-r--r--drivers/media/pci/ngene/ngene-core.c32
-rw-r--r--drivers/media/pci/ngene/ngene-i2c.c6
-rw-r--r--drivers/media/pci/ngene/ngene.h6
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c13
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-core.c1
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-g723.c32
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-i2c.c1
-rw-r--r--drivers/media/pci/ttpci/av7110.c5
-rw-r--r--drivers/media/pci/tw5864/tw5864-video.c1
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c2
-rw-r--r--drivers/media/platform/Kconfig74
-rw-r--r--drivers/media/platform/Makefile13
-rw-r--r--drivers/media/platform/am437x/Kconfig1
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c15
-rw-r--r--drivers/media/platform/atmel/Kconfig2
-rw-r--r--drivers/media/platform/atmel/atmel-isc.c36
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c35
-rw-r--r--drivers/media/platform/coda/coda-bit.c53
-rw-r--r--drivers/media/platform/coda/coda-common.c70
-rw-r--r--drivers/media/platform/coda/coda.h5
-rw-r--r--drivers/media/platform/coda/imx-vdoa.c49
-rw-r--r--drivers/media/platform/davinci/Kconfig1
-rw-r--r--drivers/media/platform/davinci/ccdc_hw_device.h10
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c92
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c151
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c93
-rw-r--r--drivers/media/platform/davinci/vpif.c57
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c234
-rw-r--r--drivers/media/platform/davinci/vpif_display.c7
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c13
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h1
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c8
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c7
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c7
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c4
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c13
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c6
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c1
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_core.c12
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c10
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h20
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c133
-rw-r--r--drivers/media/platform/omap/omap_voutdef.h6
-rw-r--r--drivers/media/platform/omap3isp/isp.c49
-rw-r--r--drivers/media/platform/pxa_camera.c77
-rw-r--r--drivers/media/platform/qcom/venus/Makefile11
-rw-r--r--drivers/media/platform/qcom/venus/core.c388
-rw-r--r--drivers/media/platform/qcom/venus/core.h323
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c98
-rw-r--r--drivers/media/platform/qcom/venus/firmware.h22
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c725
-rw-r--r--drivers/media/platform/qcom/venus/helpers.h45
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c522
-rw-r--r--drivers/media/platform/qcom/venus/hfi.h175
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c1259
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.h304
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h1050
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c1053
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.h283
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c1572
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.h23
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus_io.h113
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c1162
-rw-r--r--drivers/media/platform/qcom/venus/vdec.h23
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c158
-rw-r--r--drivers/media/platform/qcom/venus/venc.c1283
-rw-r--r--drivers/media/platform/qcom/venus/venc.h23
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c270
-rw-r--r--drivers/media/platform/rcar-fcp.c17
-rw-r--r--drivers/media/platform/rcar-vin/Kconfig1
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c66
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c230
-rw-r--r--drivers/media/platform/rcar-vin/rcar-v4l2.c97
-rw-r--r--drivers/media/platform/rcar-vin/rcar-vin.h9
-rw-r--r--drivers/media/platform/rcar_drif.c1498
-rw-r--r--drivers/media/platform/rcar_fdp1.c12
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c4
-rw-r--r--drivers/media/platform/s5p-cec/s5p_cec.c6
-rw-r--r--drivers/media/platform/s5p-cec/s5p_cec.h1
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c20
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c2
-rw-r--r--drivers/media/platform/sh_vou.c2
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c7
-rw-r--r--drivers/media/platform/soc_camera/soc_mediabus.c1
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-debug.c14
-rw-r--r--drivers/media/platform/sti/cec/stih-cec.c16
-rw-r--r--drivers/media/platform/stm32/Makefile2
-rw-r--r--drivers/media/platform/stm32/stm32-cec.c362
-rw-r--r--drivers/media/platform/stm32/stm32-dcmi.c1404
-rw-r--r--drivers/media/platform/ti-vpe/cal.c15
-rw-r--r--drivers/media/platform/video-mux.c334
-rw-r--r--drivers/media/platform/vimc/Kconfig1
-rw-r--r--drivers/media/platform/vimc/Makefile10
-rw-r--r--drivers/media/platform/vimc/vimc-capture.c322
-rw-r--r--drivers/media/platform/vimc/vimc-capture.h28
-rw-r--r--drivers/media/platform/vimc/vimc-common.c473
-rw-r--r--drivers/media/platform/vimc/vimc-common.h229
-rw-r--r--drivers/media/platform/vimc/vimc-core.c610
-rw-r--r--drivers/media/platform/vimc/vimc-core.h112
-rw-r--r--drivers/media/platform/vimc/vimc-debayer.c602
-rw-r--r--drivers/media/platform/vimc/vimc-scaler.c456
-rw-r--r--drivers/media/platform/vimc/vimc-sensor.c322
-rw-r--r--drivers/media/platform/vimc/vimc-sensor.h28
-rw-r--r--drivers/media/platform/vivid/vivid-cec.c6
-rw-r--r--drivers/media/platform/vsp1/vsp1.h1
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c23
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c41
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.h11
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c9
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.c13
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c2
-rw-r--r--drivers/media/platform/xilinx/Kconfig1
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c63
-rw-r--r--drivers/media/radio/radio-wl1273.c15
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c4
-rw-r--r--drivers/media/rc/Kconfig8
-rw-r--r--drivers/media/rc/ati_remote.c3
-rw-r--r--drivers/media/rc/iguanair.c1
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c4
-rw-r--r--drivers/media/rc/imon.c2
-rw-r--r--drivers/media/rc/ir-lirc-codec.c39
-rw-r--r--drivers/media/rc/ir-spi.c11
-rw-r--r--drivers/media/rc/lirc_dev.c254
-rw-r--r--drivers/media/rc/mceusb.c158
-rw-r--r--drivers/media/rc/meson-ir.c89
-rw-r--r--drivers/media/rc/rc-core-priv.h2
-rw-r--r--drivers/media/rc/rc-ir-raw.c36
-rw-r--r--drivers/media/rc/rc-main.c160
-rw-r--r--drivers/media/rc/sir_ir.c94
-rw-r--r--drivers/media/tuners/fc0011.c1
-rw-r--r--drivers/media/tuners/mxl5005s.c2
-rw-r--r--drivers/media/tuners/tda18271-fe.c2
-rw-r--r--drivers/media/tuners/xc5000.c27
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c30
-rw-r--r--drivers/media/usb/au0828/au0828-input.c2
-rw-r--r--drivers/media/usb/au0828/au0828.h2
-rw-r--r--drivers/media/usb/cpia2/cpia2_core.c51
-rw-r--r--drivers/media/usb/cx231xx/Kconfig2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c34
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c49
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-input.c5
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h1
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c199
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.h4
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c11
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c32
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.h8
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c38
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c1
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c5
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c22
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c35
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx.h1
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k83a.c5
-rw-r--r--drivers/media/usb/gspca/ov519.c3
-rw-r--r--drivers/media/usb/pulse8-cec/pulse8-cec.c11
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c2
-rw-r--r--drivers/media/usb/pwc/pwc-v4l.c3
-rw-r--r--drivers/media/usb/rainshadow-cec/rainshadow-cec.c28
-rw-r--r--drivers/media/usb/s2255/s2255drv.c2
-rw-r--r--drivers/media/usb/stkwebcam/stk-sensor.c32
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c70
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.h6
-rw-r--r--drivers/media/usb/tm6000/tm6000-input.c4
-rw-r--r--drivers/media/usb/usbvision/usbvision-i2c.c3
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c4
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c34
-rw-r--r--drivers/media/usb/uvc/uvc_video.c4
-rw-r--r--drivers/media/v4l2-core/Kconfig3
-rw-r--r--drivers/media/v4l2-core/Makefile4
-rw-r--r--drivers/media/v4l2-core/tuner-core.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c29
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c51
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c8
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c12
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c345
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c94
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c37
-rw-r--r--drivers/media/v4l2-core/v4l2-of.c327
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c8
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c40
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-sg.c8
268 files changed, 29573 insertions, 4928 deletions
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 9dfc79800c71..d596b601ff42 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -28,6 +28,8 @@
#include <linux/string.h>
#include <linux/types.h>
+#include <drm/drm_edid.h>
+
#include "cec-priv.h"
static void cec_fill_msg_report_features(struct cec_adapter *adap,
@@ -366,6 +368,8 @@ int cec_thread_func(void *_adap)
* transmit should be canceled.
*/
err = wait_event_interruptible_timeout(adap->kthread_waitq,
+ (adap->needs_hpd &&
+ (!adap->is_configured && !adap->is_configuring)) ||
kthread_should_stop() ||
(!adap->transmitting &&
!list_empty(&adap->transmit_queue)),
@@ -381,7 +385,9 @@ int cec_thread_func(void *_adap)
mutex_lock(&adap->lock);
- if (kthread_should_stop()) {
+ if ((adap->needs_hpd &&
+ (!adap->is_configured && !adap->is_configuring)) ||
+ kthread_should_stop()) {
cec_flush(adap);
goto unlock;
}
@@ -392,7 +398,7 @@ int cec_thread_func(void *_adap)
* happen and is an indication of a faulty CEC adapter
* driver, or the CEC bus is in some weird state.
*/
- dprintk(0, "message %*ph timed out!\n",
+ dprintk(0, "%s: message %*ph timed out!\n", __func__,
adap->transmitting->msg.len,
adap->transmitting->msg.msg);
/* Just give up on this. */
@@ -468,7 +474,7 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
struct cec_msg *msg;
u64 ts = ktime_get_ns();
- dprintk(2, "cec_transmit_done %02x\n", status);
+ dprintk(2, "%s: status %02x\n", __func__, status);
mutex_lock(&adap->lock);
data = adap->transmitting;
if (!data) {
@@ -477,7 +483,8 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
* unplugged while the transmit is ongoing. Ignore this
* transmit in that case.
*/
- dprintk(1, "cec_transmit_done without an ongoing transmit!\n");
+ dprintk(1, "%s was called without an ongoing transmit!\n",
+ __func__);
goto unlock;
}
@@ -504,6 +511,12 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
!(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
/* Retry this message */
data->attempts--;
+ if (msg->timeout)
+ dprintk(2, "retransmit: %*ph (attempts: %d, wait for 0x%02x)\n",
+ msg->len, msg->msg, data->attempts, msg->reply);
+ else
+ dprintk(2, "retransmit: %*ph (attempts: %d)\n",
+ msg->len, msg->msg, data->attempts);
/* Add the message in front of the transmit queue */
list_add(&data->list, &adap->transmit_queue);
adap->transmit_queue_sz++;
@@ -544,6 +557,32 @@ unlock:
}
EXPORT_SYMBOL_GPL(cec_transmit_done);
+void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status)
+{
+ switch (status & ~CEC_TX_STATUS_MAX_RETRIES) {
+ case CEC_TX_STATUS_OK:
+ cec_transmit_done(adap, status, 0, 0, 0, 0);
+ return;
+ case CEC_TX_STATUS_ARB_LOST:
+ cec_transmit_done(adap, status, 1, 0, 0, 0);
+ return;
+ case CEC_TX_STATUS_NACK:
+ cec_transmit_done(adap, status, 0, 1, 0, 0);
+ return;
+ case CEC_TX_STATUS_LOW_DRIVE:
+ cec_transmit_done(adap, status, 0, 0, 1, 0);
+ return;
+ case CEC_TX_STATUS_ERROR:
+ cec_transmit_done(adap, status, 0, 0, 0, 1);
+ return;
+ default:
+ /* Should never happen */
+ WARN(1, "cec-%s: invalid status 0x%02x\n", adap->name, status);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(cec_transmit_attempt_done);
+
/*
* Called when waiting for a reply times out.
*/
@@ -647,7 +686,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
return -EINVAL;
}
if (!adap->is_configured && !adap->is_configuring) {
- if (msg->msg[0] != 0xf0) {
+ if (adap->needs_hpd || msg->msg[0] != 0xf0) {
dprintk(1, "%s: adapter is unconfigured\n", __func__);
return -ENONET;
}
@@ -911,7 +950,7 @@ void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
mutex_lock(&adap->lock);
- dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg);
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
/* Check if this message was for us (directed or broadcast). */
if (!cec_msg_is_broadcast(msg))
@@ -1112,9 +1151,6 @@ static int cec_config_log_addr(struct cec_adapter *adap,
las->log_addr[idx] = log_addr;
las->log_addr_mask |= 1 << log_addr;
adap->phys_addrs[log_addr] = adap->phys_addr;
-
- dprintk(2, "claimed addr %d (%d)\n", log_addr,
- las->primary_device_type[idx]);
return 1;
}
@@ -1126,7 +1162,9 @@ static int cec_config_log_addr(struct cec_adapter *adap,
*/
static void cec_adap_unconfigure(struct cec_adapter *adap)
{
- WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
+ if (!adap->needs_hpd ||
+ adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+ WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
adap->log_addrs.log_addr_mask = 0;
adap->is_configuring = false;
adap->is_configured = false;
@@ -1300,7 +1338,7 @@ configured:
/* Report Physical Address */
cec_msg_report_physical_addr(&msg, adap->phys_addr,
las->primary_device_type[i]);
- dprintk(2, "config: la %d pa %x.%x.%x.%x\n",
+ dprintk(1, "config: la %d pa %x.%x.%x.%x\n",
las->log_addr[i],
cec_phys_addr_exp(adap->phys_addr));
cec_transmit_msg_fh(adap, &msg, NULL, false);
@@ -1355,6 +1393,8 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
if (phys_addr == adap->phys_addr || adap->devnode.unregistered)
return;
+ dprintk(1, "new physical address %x.%x.%x.%x\n",
+ cec_phys_addr_exp(phys_addr));
if (phys_addr == CEC_PHYS_ADDR_INVALID ||
adap->phys_addr != CEC_PHYS_ADDR_INVALID) {
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
@@ -1364,7 +1404,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
if (adap->monitor_all_cnt)
WARN_ON(call_op(adap, adap_monitor_all_enable, false));
mutex_lock(&adap->devnode.lock);
- if (list_empty(&adap->devnode.fhs))
+ if (adap->needs_hpd || list_empty(&adap->devnode.fhs))
WARN_ON(adap->ops->adap_enable(adap, false));
mutex_unlock(&adap->devnode.lock);
if (phys_addr == CEC_PHYS_ADDR_INVALID)
@@ -1372,7 +1412,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
}
mutex_lock(&adap->devnode.lock);
- if (list_empty(&adap->devnode.fhs) &&
+ if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) &&
adap->ops->adap_enable(adap, true)) {
mutex_unlock(&adap->devnode.lock);
return;
@@ -1380,7 +1420,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
if (adap->monitor_all_cnt &&
call_op(adap, adap_monitor_all_enable, true)) {
- if (list_empty(&adap->devnode.fhs))
+ if (adap->needs_hpd || list_empty(&adap->devnode.fhs))
WARN_ON(adap->ops->adap_enable(adap, false));
mutex_unlock(&adap->devnode.lock);
return;
@@ -1404,6 +1444,18 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
}
EXPORT_SYMBOL_GPL(cec_s_phys_addr);
+void cec_s_phys_addr_from_edid(struct cec_adapter *adap,
+ const struct edid *edid)
+{
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ if (edid && edid->extensions)
+ pa = cec_get_edid_phys_addr((const u8 *)edid,
+ EDID_LENGTH * (edid->extensions + 1), NULL);
+ cec_s_phys_addr(adap, pa, false);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr_from_edid);
+
/*
* Called from either the ioctl or a driver to set the logical addresses.
*
@@ -1534,12 +1586,12 @@ int __cec_s_log_addrs(struct cec_adapter *adap,
if (log_addrs->num_log_addrs == 2) {
if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
(1 << CEC_LOG_ADDR_TYPE_TV)))) {
- dprintk(1, "Two LAs is only allowed for audiosystem and TV\n");
+ dprintk(1, "two LAs is only allowed for audiosystem and TV\n");
return -EINVAL;
}
if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
(1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
- dprintk(1, "An audiosystem/TV can only be combined with record or playback\n");
+ dprintk(1, "an audiosystem/TV can only be combined with record or playback\n");
return -EINVAL;
}
}
@@ -1653,7 +1705,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
bool from_unregistered = init_laddr == 0xf;
struct cec_msg tx_cec_msg = { };
- dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg);
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
/* If this is a CDC-Only device, then ignore any non-CDC messages */
if (cec_is_cdc_only(&adap->log_addrs) &&
@@ -1722,7 +1774,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
if (!from_unregistered)
adap->phys_addrs[init_laddr] = pa;
- dprintk(1, "Reported physical address %x.%x.%x.%x for logical address %d\n",
+ dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n",
cec_phys_addr_exp(pa), init_laddr);
break;
}
diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c
index 999926f731c8..f7eb4c54a354 100644
--- a/drivers/media/cec/cec-api.c
+++ b/drivers/media/cec/cec-api.c
@@ -202,7 +202,8 @@ static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
err = -EPERM;
else if (adap->is_configuring)
err = -ENONET;
- else if (!adap->is_configured && msg.msg[0] != 0xf0)
+ else if (!adap->is_configured &&
+ (adap->needs_hpd || msg.msg[0] != 0xf0))
err = -ENONET;
else if (cec_is_busy(adap, fh))
err = -EBUSY;
@@ -515,6 +516,7 @@ static int cec_open(struct inode *inode, struct file *filp)
mutex_lock(&devnode->lock);
if (list_empty(&devnode->fhs) &&
+ !adap->needs_hpd &&
adap->phys_addr == CEC_PHYS_ADDR_INVALID) {
err = adap->ops->adap_enable(adap, true);
if (err) {
@@ -559,6 +561,7 @@ static int cec_release(struct inode *inode, struct file *filp)
mutex_lock(&devnode->lock);
list_del(&fh->list);
if (list_empty(&devnode->fhs) &&
+ !adap->needs_hpd &&
adap->phys_addr == CEC_PHYS_ADDR_INVALID) {
WARN_ON(adap->ops->adap_enable(adap, false));
}
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index 2f87748ba4fc..b516d599d6c4 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -230,6 +230,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
adap->capabilities = caps;
+ adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD;
adap->available_log_addrs = available_las;
adap->sequence = 0;
adap->ops = ops;
diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c
index 74dc1c32080e..08b619d0ea1e 100644
--- a/drivers/media/cec/cec-notifier.c
+++ b/drivers/media/cec/cec-notifier.c
@@ -87,6 +87,9 @@ EXPORT_SYMBOL_GPL(cec_notifier_put);
void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
{
+ if (n == NULL)
+ return;
+
mutex_lock(&n->lock);
n->phys_addr = pa;
if (n->callback)
@@ -100,6 +103,9 @@ void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
{
u16 pa = CEC_PHYS_ADDR_INVALID;
+ if (n == NULL)
+ return;
+
if (edid && edid->extensions)
pa = cec_get_edid_phys_addr((const u8 *)edid,
EDID_LENGTH * (edid->extensions + 1), NULL);
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index d38bf9bce480..17970cdd55fa 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -193,8 +193,10 @@ static void dvb_ca_private_put(struct dvb_ca_private *ca)
}
static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
-static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
-static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount);
/**
@@ -206,7 +208,7 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * e
* @nlen: Number of bytes in needle.
* @return Pointer into haystack needle was found at, or NULL if not found.
*/
-static char *findstr(char * haystack, int hlen, char * needle, int nlen)
+static char *findstr(char *haystack, int hlen, char *needle, int nlen)
{
int i;
@@ -347,7 +349,8 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
/* read the buffer size from the CAM */
if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
return ret;
- if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
+ ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ);
+ if (ret != 0)
return ret;
if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
return -EIO;
@@ -390,7 +393,8 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
* @return 0 on success, nonzero on error.
*/
static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
- int *address, int *tupleType, int *tupleLength, u8 * tuple)
+ int *address, int *tupleType,
+ int *tupleLength, u8 *tuple)
{
int i;
int _tupleType;
@@ -621,7 +625,8 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
*
* @return Number of bytes read, or < 0 on error
*/
-static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount)
{
int bytes_read;
int status;
@@ -640,72 +645,101 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * eb
}
buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
- if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ if (buf_free < (ca->slot_info[slot].link_buf_size +
+ DVB_RINGBUFFER_PKTHDRSIZE)) {
status = -EAGAIN;
goto exit;
}
}
- /* check if there is data available */
- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
- goto exit;
- if (!(status & STATUSREG_DA)) {
- /* no data */
- status = 0;
- goto exit;
- }
-
- /* read the amount of data */
- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
- goto exit;
- bytes_read = status << 8;
- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
- goto exit;
- bytes_read |= status;
+ if (ca->pub->read_data &&
+ (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT)) {
+ if (ebuf == NULL)
+ status = ca->pub->read_data(ca->pub, slot, buf,
+ sizeof(buf));
+ else
+ status = ca->pub->read_data(ca->pub, slot, buf, ecount);
+ if (status < 0)
+ return status;
+ bytes_read = status;
+ if (status == 0)
+ goto exit;
+ } else {
- /* check it will fit */
- if (ebuf == NULL) {
- if (bytes_read > ca->slot_info[slot].link_buf_size) {
- pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
- ca->dvbdev->adapter->num, bytes_read,
- ca->slot_info[slot].link_buf_size);
- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
- status = -EIO;
+ /* check if there is data available */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_STATUS);
+ if (status < 0)
goto exit;
- }
- if (bytes_read < 2) {
- pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
- ca->dvbdev->adapter->num);
- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
- status = -EIO;
+ if (!(status & STATUSREG_DA)) {
+ /* no data */
+ status = 0;
goto exit;
}
- } else {
- if (bytes_read > ecount) {
- pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
- ca->dvbdev->adapter->num);
- status = -EIO;
+
+ /* read the amount of data */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_SIZE_HIGH);
+ if (status < 0)
+ goto exit;
+ bytes_read = status << 8;
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_SIZE_LOW);
+ if (status < 0)
goto exit;
+ bytes_read |= status;
+
+ /* check it will fit */
+ if (ebuf == NULL) {
+ if (bytes_read > ca->slot_info[slot].link_buf_size) {
+ pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+ ca->dvbdev->adapter->num, bytes_read,
+ ca->slot_info[slot].link_buf_size);
+ ca->slot_info[slot].slot_state =
+ DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (bytes_read < 2) {
+ pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+ ca->dvbdev->adapter->num);
+ ca->slot_info[slot].slot_state =
+ DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ } else {
+ if (bytes_read > ecount) {
+ pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+ ca->dvbdev->adapter->num);
+ status = -EIO;
+ goto exit;
+ }
}
- }
- /* fill the buffer */
- for (i = 0; i < bytes_read; i++) {
- /* read byte and check */
- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
- goto exit;
+ /* fill the buffer */
+ for (i = 0; i < bytes_read; i++) {
+ /* read byte and check */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_DATA);
+ if (status < 0)
+ goto exit;
- /* OK, store it in the buffer */
- buf[i] = status;
- }
+ /* OK, store it in the buffer */
+ buf[i] = status;
+ }
- /* check for read error (RE should now be 0) */
- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
- goto exit;
- if (status & STATUSREG_RE) {
- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
- status = -EIO;
- goto exit;
+ /* check for read error (RE should now be 0) */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+ if (status & STATUSREG_RE) {
+ ca->slot_info[slot].slot_state =
+ DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
}
/* OK, add it to the receive buffer, or copy into external buffer if supplied */
@@ -745,7 +779,8 @@ exit:
*
* @return Number of bytes written, or < 0 on error.
*/
-static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write)
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
+ u8 *buf, int bytes_write)
{
int status;
int i;
@@ -757,6 +792,10 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b
if (bytes_write > ca->slot_info[slot].link_buf_size)
return -EINVAL;
+ if (ca->pub->write_data &&
+ (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT))
+ return ca->pub->write_data(ca->pub, slot, buf, bytes_write);
+
/* it is possible we are dealing with a single buffer implementation,
thus if there is data available for read or if there is even a read
already in progress, we do nothing but awake the kernel thread to
@@ -840,7 +879,6 @@ exit:
exitnowrite:
return status;
}
-EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
@@ -849,7 +887,7 @@ EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
/**
- * dvb_ca_en50221_camready_irq - A CAM has been removed => shut it down.
+ * dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down.
*
* @ca: CA instance.
* @slot: Slot to shut down.
@@ -870,11 +908,10 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
/* success */
return 0;
}
-EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
/**
- * dvb_ca_en50221_camready_irq - A CAMCHANGE IRQ has occurred.
+ * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred.
*
* @ca: CA instance.
* @slot: Slot concerned.
@@ -899,7 +936,7 @@ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int ch
atomic_inc(&ca->slot_info[slot].camchange_count);
dvb_ca_en50221_thread_wakeup(ca);
}
-EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
/**
@@ -919,10 +956,11 @@ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
dvb_ca_en50221_thread_wakeup(ca);
}
}
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
/**
- * An FR or DA IRQ has occurred.
+ * dvb_ca_en50221_frda_irq - An FR or DA IRQ has occurred.
*
* @ca: CA instance.
* @slot: Slot concerned.
@@ -949,7 +987,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
break;
}
}
-
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
/* ******************************************************************************** */
@@ -1172,7 +1210,8 @@ static int dvb_ca_en50221_thread(void *data)
pr_err("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n",
ca->dvbdev->adapter->num);
- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ ca->slot_info[slot].slot_state =
+ DVB_CA_SLOTSTATE_UNINITIALISED;
dvb_ca_en50221_thread_update_delay(ca);
break;
}
@@ -1345,7 +1384,8 @@ static long dvb_ca_en50221_io_ioctl(struct file *file,
* @return Number of bytes read, or <0 on error.
*/
static ssize_t dvb_ca_en50221_io_write(struct file *file,
- const char __user * buf, size_t count, loff_t * ppos)
+ const char __user *buf, size_t count,
+ loff_t *ppos)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_ca_private *ca = dvbdev->priv;
@@ -1485,8 +1525,8 @@ nextslot:
*
* @return Number of bytes read, or <0 on error.
*/
-static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
- size_t count, loff_t * ppos)
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_ca_private *ca = dvbdev->priv;
@@ -1664,7 +1704,7 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
*
* @return Standard poll mask.
*/
-static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
+static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_ca_private *ca = dvbdev->priv;
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h
index 1e4bbbd34d91..82617bac0875 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.h
+++ b/drivers/media/dvb-core/dvb_ca_en50221.h
@@ -41,6 +41,8 @@
* @write_attribute_mem: function for writing attribute memory on the CAM
* @read_cam_control: function for reading the control interface on the CAM
* @write_cam_control: function for reading the control interface on the CAM
+ * @read_data: function for reading data (block mode)
+ * @write_data: function for writing data (block mode)
* @slot_reset: function to reset the CAM slot
* @slot_shutdown: function to shutdown a CAM slot
* @slot_ts_enable: function to enable the Transport Stream on a CAM slot
@@ -66,6 +68,11 @@ struct dvb_ca_en50221 {
int (*write_cam_control)(struct dvb_ca_en50221 *ca,
int slot, u8 address, u8 value);
+ int (*read_data)(struct dvb_ca_en50221 *ca,
+ int slot, u8 *ebuf, int ecount);
+ int (*write_data)(struct dvb_ca_en50221 *ca,
+ int slot, u8 *ebuf, int ecount);
+
int (*slot_reset)(struct dvb_ca_en50221 *ca, int slot);
int (*slot_shutdown)(struct dvb_ca_en50221 *ca, int slot);
int (*slot_ts_enable)(struct dvb_ca_en50221 *ca, int slot);
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index 9947b342633e..06b0dcc13695 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -828,8 +828,7 @@ static void dvb_net_ule(struct net_device *dev, const u8 *buf, size_t buf_len)
/* Copy data into our current skb. */
h.how_much = min(h.priv->ule_sndu_remain, (int)h.ts_remain);
- memcpy(skb_put(h.priv->ule_skb, h.how_much),
- h.from_where, h.how_much);
+ skb_put_data(h.priv->ule_skb, h.from_where, h.how_much);
h.priv->ule_sndu_remain -= h.how_much;
h.ts_remain -= h.how_much;
h.from_where += h.how_much;
@@ -964,7 +963,7 @@ static void dvb_net_sec(struct net_device *dev,
skb->dev = dev;
/* copy L3 payload */
- eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14 - snap);
+ eth = skb_put(skb, pkt_len - 12 - 4 + 14 - snap);
memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap);
/* create ethernet header: */
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index e8c6554a47aa..3a260b82b3e8 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -436,6 +436,7 @@ config DVB_TDA10048
config DVB_AF9013
tristate "Afatech AF9013 demodulator"
depends on DVB_CORE && I2C
+ select REGMAP
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Say Y when you want to support this frontend.
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
index b978002af4d8..b8f3ebfc3e27 100644
--- a/drivers/media/dvb-frontends/af9013.c
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -20,13 +20,18 @@
#include "af9013_priv.h"
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE 64
-
struct af9013_state {
- struct i2c_adapter *i2c;
+ struct i2c_client *client;
+ struct regmap *regmap;
struct dvb_frontend fe;
- struct af9013_config config;
+ u32 clk;
+ u8 tuner;
+ u32 if_frequency;
+ u8 ts_mode;
+ u8 ts_output_pin;
+ bool spec_inv;
+ u8 api_version[4];
+ u8 gpio[4];
/* tuner/demod RF and IF AGC limits used for signal strength calc */
u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
@@ -44,188 +49,14 @@ struct af9013_state {
struct delayed_work statistics_work;
};
-/* write multiple registers */
-static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
- const u8 *val, int len)
-{
- int ret;
- u8 buf[MAX_XFER_SIZE];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->config.i2c_addr,
- .flags = 0,
- .len = 3 + len,
- .buf = buf,
- }
- };
-
- if (3 + len > sizeof(buf)) {
- dev_warn(&priv->i2c->dev,
- "%s: i2c wr reg=%04x: len=%d is too big!\n",
- KBUILD_MODNAME, reg, len);
- return -EINVAL;
- }
-
- buf[0] = (reg >> 8) & 0xff;
- buf[1] = (reg >> 0) & 0xff;
- buf[2] = mbox;
- memcpy(&buf[3], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%04x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
- u8 *val, int len)
-{
- int ret;
- u8 buf[3];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->config.i2c_addr,
- .flags = 0,
- .len = 3,
- .buf = buf,
- }, {
- .addr = priv->config.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- }
- };
-
- buf[0] = (reg >> 8) & 0xff;
- buf[1] = (reg >> 0) & 0xff;
- buf[2] = mbox;
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%04x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* write multiple registers */
-static int af9013_wr_regs(struct af9013_state *priv, u16 reg, const u8 *val,
- int len)
-{
- int ret, i;
- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(1 << 0);
-
- if ((priv->config.ts_mode == AF9013_TS_USB) &&
- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
- mbox |= ((len - 1) << 2);
- ret = af9013_wr_regs_i2c(priv, mbox, reg, val, len);
- } else {
- for (i = 0; i < len; i++) {
- ret = af9013_wr_regs_i2c(priv, mbox, reg+i, val+i, 1);
- if (ret)
- goto err;
- }
- }
-
-err:
- return 0;
-}
-
-/* read multiple registers */
-static int af9013_rd_regs(struct af9013_state *priv, u16 reg, u8 *val, int len)
-{
- int ret, i;
- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(0 << 0);
-
- if ((priv->config.ts_mode == AF9013_TS_USB) &&
- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
- mbox |= ((len - 1) << 2);
- ret = af9013_rd_regs_i2c(priv, mbox, reg, val, len);
- } else {
- for (i = 0; i < len; i++) {
- ret = af9013_rd_regs_i2c(priv, mbox, reg+i, val+i, 1);
- if (ret)
- goto err;
- }
- }
-
-err:
- return 0;
-}
-
-/* write single register */
-static int af9013_wr_reg(struct af9013_state *priv, u16 reg, u8 val)
-{
- return af9013_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int af9013_rd_reg(struct af9013_state *priv, u16 reg, u8 *val)
-{
- return af9013_rd_regs(priv, reg, val, 1);
-}
-
-static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
- u8 len)
-{
- u8 mbox = (1 << 7)|(1 << 6)|((len - 1) << 2)|(1 << 1)|(1 << 0);
- return af9013_wr_regs_i2c(state, mbox, reg, val, len);
-}
-
-static int af9013_wr_reg_bits(struct af9013_state *state, u16 reg, int pos,
- int len, u8 val)
-{
- int ret;
- u8 tmp, mask;
-
- /* no need for read if whole reg is written */
- if (len != 8) {
- ret = af9013_rd_reg(state, reg, &tmp);
- if (ret)
- return ret;
-
- mask = (0xff >> (8 - len)) << pos;
- val <<= pos;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return af9013_wr_reg(state, reg, val);
-}
-
-static int af9013_rd_reg_bits(struct af9013_state *state, u16 reg, int pos,
- int len, u8 *val)
-{
- int ret;
- u8 tmp;
-
- ret = af9013_rd_reg(state, reg, &tmp);
- if (ret)
- return ret;
-
- *val = (tmp >> pos);
- *val &= (0xff >> (8 - len));
-
- return 0;
-}
-
static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
{
+ struct i2c_client *client = state->client;
int ret;
u8 pos;
u16 addr;
- dev_dbg(&state->i2c->dev, "%s: gpio=%d gpioval=%02x\n",
- __func__, gpio, gpioval);
+ dev_dbg(&client->dev, "gpio %u, gpioval %02x\n", gpio, gpioval);
/*
* GPIO0 & GPIO1 0xd735
@@ -243,8 +74,6 @@ static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
break;
default:
- dev_err(&state->i2c->dev, "%s: invalid gpio=%d\n",
- KBUILD_MODNAME, gpio);
ret = -EINVAL;
goto err;
}
@@ -261,197 +90,124 @@ static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
break;
}
- ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval);
+ ret = regmap_update_bits(state->regmap, addr, 0x0f << pos,
+ gpioval << pos);
if (ret)
goto err;
- return ret;
-err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static u32 af9013_div(struct af9013_state *state, u32 a, u32 b, u32 x)
-{
- u32 r = 0, c = 0, i;
-
- dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x);
-
- if (a > b) {
- c = a / b;
- a = a - c * b;
- }
-
- for (i = 0; i < x; i++) {
- if (a >= b) {
- r += 1;
- a -= b;
- }
- a <<= 1;
- r <<= 1;
- }
- r = (c << (u32)x) + r;
-
- dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n",
- __func__, a, b, x, r, r);
-
- return r;
-}
-
-static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
-{
- int ret, i;
- u8 tmp;
-
- dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff);
-
- /* enable reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 1);
- if (ret)
- goto err;
-
- /* start reset mechanism */
- ret = af9013_wr_reg(state, 0xaeff, 1);
- if (ret)
- goto err;
-
- /* wait reset performs */
- for (i = 0; i < 150; i++) {
- ret = af9013_rd_reg_bits(state, 0xd417, 1, 1, &tmp);
- if (ret)
- goto err;
-
- if (tmp)
- break; /* reset done */
-
- usleep_range(5000, 25000);
- }
-
- if (!tmp)
- return -ETIMEDOUT;
-
- if (onoff) {
- /* clear reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 1, 1, 0);
- if (ret)
- goto err;
-
- /* disable reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 0);
-
- /* power on */
- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 0);
- } else {
- /* power off */
- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 1);
- }
-
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
/* reset and start BER counter */
- ret = af9013_wr_reg_bits(state, 0xd391, 4, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0xd391, 0x10, 0x10);
if (ret)
goto err;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
+ unsigned int utmp;
u8 buf[5];
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
/* check if error bit count is ready */
- ret = af9013_rd_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+ ret = regmap_read(state->regmap, 0xd391, &utmp);
if (ret)
goto err;
- if (!buf[0]) {
- dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__);
+ if (!((utmp >> 4) & 0x01)) {
+ dev_dbg(&client->dev, "not ready\n");
return 0;
}
- ret = af9013_rd_regs(state, 0xd387, buf, 5);
+ ret = regmap_bulk_read(state->regmap, 0xd387, buf, 5);
if (ret)
goto err;
state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0];
state->ucblocks += (buf[4] << 8) | buf[3];
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_statistics_snr_start(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
/* start SNR meas */
- ret = af9013_wr_reg_bits(state, 0xd2e1, 3, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0xd2e1, 0x08, 0x08);
if (ret)
goto err;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_statistics_snr_result(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret, i, len;
- u8 buf[3], tmp;
+ unsigned int utmp;
+ u8 buf[3];
u32 snr_val;
const struct af9013_snr *uninitialized_var(snr_lut);
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
/* check if SNR ready */
- ret = af9013_rd_reg_bits(state, 0xd2e1, 3, 1, &tmp);
+ ret = regmap_read(state->regmap, 0xd2e1, &utmp);
if (ret)
goto err;
- if (!tmp) {
- dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__);
+ if (!((utmp >> 3) & 0x01)) {
+ dev_dbg(&client->dev, "not ready\n");
return 0;
}
/* read value */
- ret = af9013_rd_regs(state, 0xd2e3, buf, 3);
+ ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
if (ret)
goto err;
snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
/* read current modulation */
- ret = af9013_rd_reg(state, 0xd3c1, &tmp);
+ ret = regmap_read(state->regmap, 0xd3c1, &utmp);
if (ret)
goto err;
- switch ((tmp >> 6) & 3) {
+ switch ((utmp >> 6) & 3) {
case 0:
len = ARRAY_SIZE(qpsk_snr_lut);
snr_lut = qpsk_snr_lut;
@@ -469,32 +225,36 @@ static int af9013_statistics_snr_result(struct dvb_frontend *fe)
}
for (i = 0; i < len; i++) {
- tmp = snr_lut[i].snr;
+ utmp = snr_lut[i].snr;
if (snr_val < snr_lut[i].val)
break;
}
- state->snr = tmp * 10; /* dB/10 */
+ state->snr = utmp * 10; /* dB/10 */
- return ret;
+ c->cnr.stat[0].svalue = 1000 * utmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret = 0;
u8 buf[2], rf_gain, if_gain;
int signal_strength;
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
if (!state->signal_strength_en)
return 0;
- ret = af9013_rd_regs(state, 0xd07c, buf, 2);
+ ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
if (ret)
goto err;
@@ -513,9 +273,9 @@ static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
state->signal_strength = signal_strength;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
@@ -535,6 +295,7 @@ static void af9013_statistics_work(struct work_struct *work)
switch (state->statistics_step) {
default:
state->statistics_step = 0;
+ /* fall-through */
case 0:
af9013_statistics_signal_strength(&state->fe);
state->statistics_step++;
@@ -579,61 +340,72 @@ static int af9013_get_tune_settings(struct dvb_frontend *fe,
static int af9013_set_frontend(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret, i, sampling_freq;
bool auto_mode, spec_inv;
u8 buf[6];
u32 if_frequency, freq_cw;
- dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
- __func__, c->frequency, c->bandwidth_hz);
+ dev_dbg(&client->dev, "frequency %u, bandwidth_hz %u\n",
+ c->frequency, c->bandwidth_hz);
/* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
/* program CFOE coefficients */
if (c->bandwidth_hz != state->bandwidth_hz) {
for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
- if (coeff_lut[i].clock == state->config.clock &&
+ if (coeff_lut[i].clock == state->clk &&
coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
break;
}
}
/* Return an error if can't find bandwidth or the right clock */
- if (i == ARRAY_SIZE(coeff_lut))
- return -EINVAL;
+ if (i == ARRAY_SIZE(coeff_lut)) {
+ ret = -EINVAL;
+ goto err;
+ }
- ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val,
- sizeof(coeff_lut[i].val));
+ ret = regmap_bulk_write(state->regmap, 0xae00, coeff_lut[i].val,
+ sizeof(coeff_lut[i].val));
+ if (ret)
+ goto err;
}
/* program frequency control */
if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) {
/* get used IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency)
- fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
- else
- if_frequency = state->config.if_frequency;
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe,
+ &if_frequency);
+ if (ret)
+ goto err;
+ } else {
+ if_frequency = state->if_frequency;
+ }
- dev_dbg(&state->i2c->dev, "%s: if_frequency=%d\n",
- __func__, if_frequency);
+ dev_dbg(&client->dev, "if_frequency %u\n", if_frequency);
sampling_freq = if_frequency;
- while (sampling_freq > (state->config.clock / 2))
- sampling_freq -= state->config.clock;
+ while (sampling_freq > (state->clk / 2))
+ sampling_freq -= state->clk;
if (sampling_freq < 0) {
sampling_freq *= -1;
- spec_inv = state->config.spec_inv;
+ spec_inv = state->spec_inv;
} else {
- spec_inv = !state->config.spec_inv;
+ spec_inv = !state->spec_inv;
}
- freq_cw = af9013_div(state, sampling_freq, state->config.clock,
- 23);
+ freq_cw = DIV_ROUND_CLOSEST_ULL((u64)sampling_freq * 0x800000,
+ state->clk);
if (spec_inv)
freq_cw = 0x800000 - freq_cw;
@@ -648,32 +420,32 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[4] = (freq_cw >> 8) & 0xff;
buf[5] = (freq_cw >> 16) & 0x7f;
- ret = af9013_wr_regs(state, 0xd140, buf, 3);
+ ret = regmap_bulk_write(state->regmap, 0xd140, buf, 3);
if (ret)
goto err;
- ret = af9013_wr_regs(state, 0x9be7, buf, 6);
+ ret = regmap_bulk_write(state->regmap, 0x9be7, buf, 6);
if (ret)
goto err;
}
/* clear TPS lock flag */
- ret = af9013_wr_reg_bits(state, 0xd330, 3, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0xd330, 0x08, 0x08);
if (ret)
goto err;
/* clear MPEG2 lock flag */
- ret = af9013_wr_reg_bits(state, 0xd507, 6, 1, 0);
+ ret = regmap_update_bits(state->regmap, 0xd507, 0x40, 0x00);
if (ret)
goto err;
/* empty channel function */
- ret = af9013_wr_reg_bits(state, 0x9bfe, 0, 1, 0);
+ ret = regmap_update_bits(state->regmap, 0x9bfe, 0x01, 0x00);
if (ret)
goto err;
/* empty DVB-T channel function */
- ret = af9013_wr_reg_bits(state, 0x9bc2, 0, 1, 0);
+ ret = regmap_update_bits(state->regmap, 0x9bc2, 0x01, 0x00);
if (ret)
goto err;
@@ -691,8 +463,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[0] |= (1 << 0);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid transmission_mode\n",
- __func__);
+ dev_dbg(&client->dev, "invalid transmission_mode\n");
auto_mode = true;
}
@@ -712,8 +483,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[0] |= (3 << 2);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid guard_interval\n",
- __func__);
+ dev_dbg(&client->dev, "invalid guard_interval\n");
auto_mode = true;
}
@@ -733,7 +503,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[0] |= (3 << 4);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__);
+ dev_dbg(&client->dev, "invalid hierarchy\n");
auto_mode = true;
}
@@ -750,7 +520,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[1] |= (2 << 6);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid modulation\n", __func__);
+ dev_dbg(&client->dev, "invalid modulation\n");
auto_mode = true;
}
@@ -776,8 +546,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[2] |= (4 << 0);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid code_rate_HP\n",
- __func__);
+ dev_dbg(&client->dev, "invalid code_rate_HP\n");
auto_mode = true;
}
@@ -802,8 +571,7 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
case FEC_NONE:
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid code_rate_LP\n",
- __func__);
+ dev_dbg(&client->dev, "invalid code_rate_LP\n");
auto_mode = true;
}
@@ -817,38 +585,37 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
buf[1] |= (2 << 2);
break;
default:
- dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n",
- __func__);
+ dev_dbg(&client->dev, "invalid bandwidth_hz\n");
ret = -EINVAL;
goto err;
}
- ret = af9013_wr_regs(state, 0xd3c0, buf, 3);
+ ret = regmap_bulk_write(state->regmap, 0xd3c0, buf, 3);
if (ret)
goto err;
if (auto_mode) {
/* clear easy mode flag */
- ret = af9013_wr_reg(state, 0xaefd, 0);
+ ret = regmap_write(state->regmap, 0xaefd, 0x00);
if (ret)
goto err;
- dev_dbg(&state->i2c->dev, "%s: auto params\n", __func__);
+ dev_dbg(&client->dev, "auto params\n");
} else {
/* set easy mode flag */
- ret = af9013_wr_reg(state, 0xaefd, 1);
+ ret = regmap_write(state->regmap, 0xaefd, 0x01);
if (ret)
goto err;
- ret = af9013_wr_reg(state, 0xaefe, 0);
+ ret = regmap_write(state->regmap, 0xaefe, 0x00);
if (ret)
goto err;
- dev_dbg(&state->i2c->dev, "%s: manual params\n", __func__);
+ dev_dbg(&client->dev, "manual params\n");
}
- /* tune */
- ret = af9013_wr_reg(state, 0xffff, 0);
+ /* Reset FSM */
+ ret = regmap_write(state->regmap, 0xffff, 0x00);
if (ret)
goto err;
@@ -856,9 +623,9 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
state->set_frontend_jiffies = jiffies;
state->first_tune = false;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
@@ -866,12 +633,13 @@ static int af9013_get_frontend(struct dvb_frontend *fe,
struct dtv_frontend_properties *c)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
u8 buf[3];
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
- ret = af9013_rd_regs(state, 0xd3c0, buf, 3);
+ ret = regmap_bulk_read(state->regmap, 0xd3c0, buf, 3);
if (ret)
goto err;
@@ -973,17 +741,18 @@ static int af9013_get_frontend(struct dvb_frontend *fe,
break;
}
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
- u8 tmp;
+ unsigned int utmp;
/*
* Return status from the cache if it is younger than 2000ms with the
@@ -1001,21 +770,21 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
}
/* MPEG2 lock */
- ret = af9013_rd_reg_bits(state, 0xd507, 6, 1, &tmp);
+ ret = regmap_read(state->regmap, 0xd507, &utmp);
if (ret)
goto err;
- if (tmp)
+ if ((utmp >> 6) & 0x01)
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
FE_HAS_SYNC | FE_HAS_LOCK;
if (!*status) {
/* TPS lock */
- ret = af9013_rd_reg_bits(state, 0xd330, 3, 1, &tmp);
+ ret = regmap_read(state->regmap, 0xd330, &utmp);
if (ret)
goto err;
- if (tmp)
+ if ((utmp >> 3) & 0x01)
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI;
}
@@ -1023,9 +792,9 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
state->fe_status = *status;
state->read_status_jiffies = jiffies;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
@@ -1060,118 +829,82 @@ static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
static int af9013_init(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret, i, len;
- u8 buf[3], tmp;
- u32 adc_cw;
+ unsigned int utmp;
+ u8 buf[3];
const struct af9013_reg_bit *init;
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
+
+ /* ADC on */
+ ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x00);
+ if (ret)
+ goto err;
- /* power on */
- ret = af9013_power_ctrl(state, 1);
+ /* Clear reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x02, 0x00);
if (ret)
goto err;
- /* enable ADC */
- ret = af9013_wr_reg(state, 0xd73a, 0xa4);
+ /* Disable reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x00);
if (ret)
goto err;
/* write API version to firmware */
- ret = af9013_wr_regs(state, 0x9bf2, state->config.api_version, 4);
+ ret = regmap_bulk_write(state->regmap, 0x9bf2, state->api_version, 4);
if (ret)
goto err;
/* program ADC control */
- switch (state->config.clock) {
+ switch (state->clk) {
case 28800000: /* 28.800 MHz */
- tmp = 0;
+ utmp = 0;
break;
case 20480000: /* 20.480 MHz */
- tmp = 1;
+ utmp = 1;
break;
case 28000000: /* 28.000 MHz */
- tmp = 2;
+ utmp = 2;
break;
case 25000000: /* 25.000 MHz */
- tmp = 3;
+ utmp = 3;
break;
default:
- dev_err(&state->i2c->dev, "%s: invalid clock\n",
- KBUILD_MODNAME);
- return -EINVAL;
- }
-
- adc_cw = af9013_div(state, state->config.clock, 1000000ul, 19);
- buf[0] = (adc_cw >> 0) & 0xff;
- buf[1] = (adc_cw >> 8) & 0xff;
- buf[2] = (adc_cw >> 16) & 0xff;
-
- ret = af9013_wr_regs(state, 0xd180, buf, 3);
- if (ret)
- goto err;
-
- ret = af9013_wr_reg_bits(state, 0x9bd2, 0, 4, tmp);
- if (ret)
- goto err;
-
- /* set I2C master clock */
- ret = af9013_wr_reg(state, 0xd416, 0x14);
- if (ret)
- goto err;
-
- /* set 16 embx */
- ret = af9013_wr_reg_bits(state, 0xd700, 1, 1, 1);
- if (ret)
- goto err;
-
- /* set no trigger */
- ret = af9013_wr_reg_bits(state, 0xd700, 2, 1, 0);
- if (ret)
+ ret = -EINVAL;
goto err;
+ }
- /* set read-update bit for constellation */
- ret = af9013_wr_reg_bits(state, 0xd371, 1, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0x9bd2, 0x0f, utmp);
if (ret)
goto err;
- /* settings for mp2if */
- if (state->config.ts_mode == AF9013_TS_USB) {
- /* AF9015 split PSB to 1.5k + 0.5k */
- ret = af9013_wr_reg_bits(state, 0xd50b, 2, 1, 1);
- if (ret)
- goto err;
- } else {
- /* AF9013 change the output bit to data7 */
- ret = af9013_wr_reg_bits(state, 0xd500, 3, 1, 1);
- if (ret)
- goto err;
-
- /* AF9013 set mpeg to full speed */
- ret = af9013_wr_reg_bits(state, 0xd502, 4, 1, 1);
- if (ret)
- goto err;
- }
-
- ret = af9013_wr_reg_bits(state, 0xd520, 4, 1, 1);
+ utmp = div_u64((u64)state->clk * 0x80000, 1000000);
+ buf[0] = (utmp >> 0) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 16) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd180, buf, 3);
if (ret)
goto err;
/* load OFSM settings */
- dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__);
+ dev_dbg(&client->dev, "load ofsm settings\n");
len = ARRAY_SIZE(ofsm_init);
init = ofsm_init;
for (i = 0; i < len; i++) {
- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
- init[i].len, init[i].val);
+ u16 reg = init[i].addr;
+ u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
+ u8 val = init[i].val << init[i].pos;
+
+ ret = regmap_update_bits(state->regmap, reg, mask, val);
if (ret)
goto err;
}
/* load tuner specific settings */
- dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n",
- __func__);
- switch (state->config.tuner) {
+ dev_dbg(&client->dev, "load tuner specific settings\n");
+ switch (state->tuner) {
case AF9013_TUNER_MXL5003D:
len = ARRAY_SIZE(tuner_init_mxl5003d);
init = tuner_init_mxl5003d;
@@ -1216,98 +949,126 @@ static int af9013_init(struct dvb_frontend *fe)
}
for (i = 0; i < len; i++) {
- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
- init[i].len, init[i].val);
+ u16 reg = init[i].addr;
+ u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
+ u8 val = init[i].val << init[i].pos;
+
+ ret = regmap_update_bits(state->regmap, reg, mask, val);
if (ret)
goto err;
}
- /* TS mode */
- ret = af9013_wr_reg_bits(state, 0xd500, 1, 2, state->config.ts_mode);
+ /* TS interface */
+ if (state->ts_output_pin == 7)
+ utmp = 1 << 3 | state->ts_mode << 1;
+ else
+ utmp = 0 << 3 | state->ts_mode << 1;
+ ret = regmap_update_bits(state->regmap, 0xd500, 0x0e, utmp);
if (ret)
goto err;
/* enable lock led */
- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x01);
if (ret)
goto err;
/* check if we support signal strength */
if (!state->signal_strength_en) {
- ret = af9013_rd_reg_bits(state, 0x9bee, 0, 1,
- &state->signal_strength_en);
+ ret = regmap_read(state->regmap, 0x9bee, &utmp);
if (ret)
goto err;
+
+ state->signal_strength_en = (utmp >> 0) & 0x01;
}
/* read values needed for signal strength calculation */
if (state->signal_strength_en && !state->rf_50) {
- ret = af9013_rd_reg(state, 0x9bbd, &state->rf_50);
+ ret = regmap_bulk_read(state->regmap, 0x9bbd, &state->rf_50, 1);
if (ret)
goto err;
-
- ret = af9013_rd_reg(state, 0x9bd0, &state->rf_80);
+ ret = regmap_bulk_read(state->regmap, 0x9bd0, &state->rf_80, 1);
if (ret)
goto err;
-
- ret = af9013_rd_reg(state, 0x9be2, &state->if_50);
+ ret = regmap_bulk_read(state->regmap, 0x9be2, &state->if_50, 1);
if (ret)
goto err;
-
- ret = af9013_rd_reg(state, 0x9be4, &state->if_80);
+ ret = regmap_bulk_read(state->regmap, 0x9be4, &state->if_80, 1);
if (ret)
goto err;
}
/* SNR */
- ret = af9013_wr_reg(state, 0xd2e2, 1);
+ ret = regmap_write(state->regmap, 0xd2e2, 0x01);
if (ret)
goto err;
/* BER / UCB */
buf[0] = (10000 >> 0) & 0xff;
buf[1] = (10000 >> 8) & 0xff;
- ret = af9013_wr_regs(state, 0xd385, buf, 2);
+ ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
if (ret)
goto err;
/* enable FEC monitor */
- ret = af9013_wr_reg_bits(state, 0xd392, 1, 1, 1);
+ ret = regmap_update_bits(state->regmap, 0xd392, 0x02, 0x02);
if (ret)
goto err;
state->first_tune = true;
schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400));
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static int af9013_sleep(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
int ret;
+ unsigned int utmp;
- dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&client->dev, "\n");
/* stop statistics polling */
cancel_delayed_work_sync(&state->statistics_work);
/* disable lock led */
- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 0);
+ ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00);
if (ret)
goto err;
- /* power off */
- ret = af9013_power_ctrl(state, 0);
+ /* Enable reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x10);
if (ret)
goto err;
- return ret;
+ /* Start reset execution */
+ ret = regmap_write(state->regmap, 0xaeff, 0x01);
+ if (ret)
+ goto err;
+
+ /* Wait reset performs */
+ ret = regmap_read_poll_timeout(state->regmap, 0xd417, utmp,
+ (utmp >> 1) & 0x01, 5000, 1000000);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 1) & 0x01)) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* ADC off */
+ ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
@@ -1315,200 +1076,174 @@ static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
int ret;
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
- dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable);
+ dev_dbg(&client->dev, "enable %d\n", enable);
/* gate already open or close */
if (state->i2c_gate_state == enable)
return 0;
- if (state->config.ts_mode == AF9013_TS_USB)
- ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable);
+ if (state->ts_mode == AF9013_TS_MODE_USB)
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x08,
+ enable << 3);
else
- ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable);
+ ret = regmap_update_bits(state->regmap, 0xd607, 0x04,
+ enable << 2);
if (ret)
goto err;
state->i2c_gate_state = enable;
- return ret;
+ return 0;
err:
- dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
static void af9013_release(struct dvb_frontend *fe)
{
struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
- /* stop statistics polling */
- cancel_delayed_work_sync(&state->statistics_work);
+ dev_dbg(&client->dev, "\n");
- kfree(state);
+ i2c_unregister_device(client);
}
static const struct dvb_frontend_ops af9013_ops;
static int af9013_download_firmware(struct af9013_state *state)
{
- int i, len, remaining, ret;
- const struct firmware *fw;
+ struct i2c_client *client = state->client;
+ int ret, i, len, rem;
+ unsigned int utmp;
+ u8 buf[4];
u16 checksum = 0;
- u8 val;
- u8 fw_params[4];
- u8 *fw_file = AF9013_FIRMWARE;
+ const struct firmware *firmware;
+ const char *name = AF9013_FIRMWARE;
- msleep(100);
- /* check whether firmware is already running */
- ret = af9013_rd_reg(state, 0x98be, &val);
+ dev_dbg(&client->dev, "\n");
+
+ /* Check whether firmware is already running */
+ ret = regmap_read(state->regmap, 0x98be, &utmp);
if (ret)
goto err;
- else
- dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n",
- __func__, val);
- if (val == 0x0c) /* fw is running, no need for download */
- goto exit;
+ dev_dbg(&client->dev, "firmware status %02x\n", utmp);
- dev_info(&state->i2c->dev, "%s: found a '%s' in cold state, will try " \
- "to load a firmware\n",
- KBUILD_MODNAME, af9013_ops.info.name);
+ if (utmp == 0x0c)
+ return 0;
- /* request the firmware, this will block and timeout */
- ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
+ dev_info(&client->dev, "found a '%s' in cold state, will try to load a firmware\n",
+ af9013_ops.info.name);
+
+ /* Request the firmware, will block and timeout */
+ ret = request_firmware(&firmware, name, &client->dev);
if (ret) {
- dev_info(&state->i2c->dev, "%s: did not find the firmware " \
- "file. (%s) Please see linux/Documentation/dvb/ for " \
- "more details on firmware-problems. (%d)\n",
- KBUILD_MODNAME, fw_file, ret);
+ dev_info(&client->dev, "firmware file '%s' not found %d\n",
+ name, ret);
goto err;
}
- dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s'\n",
- KBUILD_MODNAME, fw_file);
-
- /* calc checksum */
- for (i = 0; i < fw->size; i++)
- checksum += fw->data[i];
+ dev_info(&client->dev, "downloading firmware from file '%s'\n",
+ name);
- fw_params[0] = checksum >> 8;
- fw_params[1] = checksum & 0xff;
- fw_params[2] = fw->size >> 8;
- fw_params[3] = fw->size & 0xff;
+ /* Write firmware checksum & size */
+ for (i = 0; i < firmware->size; i++)
+ checksum += firmware->data[i];
- /* write fw checksum & size */
- ret = af9013_write_ofsm_regs(state, 0x50fc,
- fw_params, sizeof(fw_params));
+ buf[0] = (checksum >> 8) & 0xff;
+ buf[1] = (checksum >> 0) & 0xff;
+ buf[2] = (firmware->size >> 8) & 0xff;
+ buf[3] = (firmware->size >> 0) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0x50fc, buf, 4);
if (ret)
- goto err_release;
-
- #define FW_ADDR 0x5100 /* firmware start address */
- #define LEN_MAX 16 /* max packet size */
- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
- len = remaining;
- if (len > LEN_MAX)
- len = LEN_MAX;
-
- ret = af9013_write_ofsm_regs(state,
- FW_ADDR + fw->size - remaining,
- (u8 *) &fw->data[fw->size - remaining], len);
+ goto err_release_firmware;
+
+ /* Download firmware */
+ #define LEN_MAX 16
+ for (rem = firmware->size; rem > 0; rem -= LEN_MAX) {
+ len = min(LEN_MAX, rem);
+ ret = regmap_bulk_write(state->regmap,
+ 0x5100 + firmware->size - rem,
+ &firmware->data[firmware->size - rem],
+ len);
if (ret) {
- dev_err(&state->i2c->dev,
- "%s: firmware download failed=%d\n",
- KBUILD_MODNAME, ret);
- goto err_release;
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
}
}
- /* request boot firmware */
- ret = af9013_wr_reg(state, 0xe205, 1);
- if (ret)
- goto err_release;
-
- for (i = 0; i < 15; i++) {
- msleep(100);
+ release_firmware(firmware);
- /* check firmware status */
- ret = af9013_rd_reg(state, 0x98be, &val);
- if (ret)
- goto err_release;
+ /* Boot firmware */
+ ret = regmap_write(state->regmap, 0xe205, 0x01);
+ if (ret)
+ goto err;
- dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n",
- __func__, val);
+ /* Check firmware status. 0c=OK, 04=fail */
+ ret = regmap_read_poll_timeout(state->regmap, 0x98be, utmp,
+ (utmp == 0x0c || utmp == 0x04),
+ 5000, 1000000);
+ if (ret)
+ goto err;
- if (val == 0x0c || val == 0x04) /* success or fail */
- break;
- }
+ dev_dbg(&client->dev, "firmware status %02x\n", utmp);
- if (val == 0x04) {
- dev_err(&state->i2c->dev, "%s: firmware did not run\n",
- KBUILD_MODNAME);
+ if (utmp == 0x04) {
ret = -ENODEV;
- } else if (val != 0x0c) {
- dev_err(&state->i2c->dev, "%s: firmware boot timeout\n",
- KBUILD_MODNAME);
+ dev_err(&client->dev, "firmware did not run\n");
+ goto err;
+ } else if (utmp != 0x0c) {
ret = -ENODEV;
+ dev_err(&client->dev, "firmware boot timeout\n");
+ goto err;
}
-err_release:
- release_firmware(fw);
+ dev_info(&client->dev, "found a '%s' in warm state\n",
+ af9013_ops.info.name);
+
+ return 0;
+err_release_firmware:
+ release_firmware(firmware);
err:
-exit:
- if (!ret)
- dev_info(&state->i2c->dev, "%s: found a '%s' in warm state\n",
- KBUILD_MODNAME, af9013_ops.info.name);
+ dev_dbg(&client->dev, "failed %d\n", ret);
return ret;
}
+/*
+ * XXX: That is wrapper to af9013_probe() via driver core in order to provide
+ * proper I2C client for legacy media attach binding.
+ * New users must use I2C client binding directly!
+ */
struct dvb_frontend *af9013_attach(const struct af9013_config *config,
- struct i2c_adapter *i2c)
+ struct i2c_adapter *i2c)
{
- int ret;
- struct af9013_state *state = NULL;
- u8 buf[4], i;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
- if (state == NULL)
- goto err;
-
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->config, config, sizeof(struct af9013_config));
-
- /* download firmware */
- if (state->config.ts_mode != AF9013_TS_USB) {
- ret = af9013_download_firmware(state);
- if (ret)
- goto err;
- }
-
- /* firmware version */
- ret = af9013_rd_regs(state, 0x5103, buf, 4);
- if (ret)
- goto err;
-
- dev_info(&state->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
- KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
-
- /* set GPIOs */
- for (i = 0; i < sizeof(state->config.gpio); i++) {
- ret = af9013_set_gpio(state, i, state->config.gpio[i]);
- if (ret)
- goto err;
- }
-
- /* create dvb_frontend */
- memcpy(&state->fe.ops, &af9013_ops,
- sizeof(struct dvb_frontend_ops));
- state->fe.demodulator_priv = state;
-
- INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
-
- return &state->fe;
-err:
- kfree(state);
- return NULL;
+ struct i2c_client *client;
+ struct i2c_board_info board_info;
+ struct af9013_platform_data pdata;
+
+ pdata.clk = config->clock;
+ pdata.tuner = config->tuner;
+ pdata.if_frequency = config->if_frequency;
+ pdata.ts_mode = config->ts_mode;
+ pdata.ts_output_pin = 7;
+ pdata.spec_inv = config->spec_inv;
+ memcpy(&pdata.api_version, config->api_version, sizeof(pdata.api_version));
+ memcpy(&pdata.gpio, config->gpio, sizeof(pdata.gpio));
+ pdata.attach_in_use = true;
+
+ memset(&board_info, 0, sizeof(board_info));
+ strlcpy(board_info.type, "af9013", sizeof(board_info.type));
+ board_info.addr = config->i2c_addr;
+ board_info.platform_data = &pdata;
+ client = i2c_new_device(i2c, &board_info);
+ if (!client || !client->dev.driver)
+ return NULL;
+
+ return pdata.get_dvb_frontend(client);
}
EXPORT_SYMBOL(af9013_attach);
@@ -1555,6 +1290,279 @@ static const struct dvb_frontend_ops af9013_ops = {
.i2c_gate_ctrl = af9013_i2c_gate_ctrl,
};
+static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &state->fe;
+}
+
+/* Own I2C access routines needed for regmap as chip uses extra command byte */
+static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
+ const u8 *val, int len)
+{
+ int ret;
+ u8 buf[21];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3 + len,
+ .buf = buf,
+ }
+ };
+
+ if (3 + len > sizeof(buf)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = cmd;
+ memcpy(&buf[3], val, len);
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0) {
+ goto err;
+ } else if (ret != 1) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
+ u8 *val, int len)
+{
+ int ret;
+ u8 buf[3];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = cmd;
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0) {
+ goto err;
+ } else if (ret != 2) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *client = context;
+ struct af9013_state *state = i2c_get_clientdata(client);
+ int ret, i;
+ u8 cmd;
+ u16 reg = ((u8 *)data)[0] << 8|((u8 *)data)[1] << 0;
+ u8 *val = &((u8 *)data)[2];
+ const unsigned int len = count - 2;
+
+ if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
+ cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0;
+ ret = af9013_wregs(client, cmd, reg, val, len);
+ if (ret)
+ goto err;
+ } else if (reg >= 0x5100 && reg < 0x8fff) {
+ /* Firmware download */
+ cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0;
+ ret = af9013_wregs(client, cmd, reg, val, len);
+ if (ret)
+ goto err;
+ } else {
+ cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0;
+ for (i = 0; i < len; i++) {
+ ret = af9013_wregs(client, cmd, reg + i, val + i, 1);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct i2c_client *client = context;
+ struct af9013_state *state = i2c_get_clientdata(client);
+ int ret, i;
+ u8 cmd;
+ u16 reg = ((u8 *)reg_buf)[0] << 8|((u8 *)reg_buf)[1] << 0;
+ u8 *val = &((u8 *)val_buf)[0];
+ const unsigned int len = val_size;
+
+ if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
+ cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0;
+ ret = af9013_rregs(client, cmd, reg, val_buf, len);
+ if (ret)
+ goto err;
+ } else {
+ cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0;
+ for (i = 0; i < len; i++) {
+ ret = af9013_rregs(client, cmd, reg + i, val + i, 1);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct af9013_state *state;
+ struct af9013_platform_data *pdata = client->dev.platform_data;
+ struct dtv_frontend_properties *c;
+ int ret, i;
+ u8 firmware_version[4];
+ static const struct regmap_bus regmap_bus = {
+ .read = af9013_regmap_read,
+ .write = af9013_regmap_write,
+ };
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ };
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Setup the state */
+ state->client = client;
+ i2c_set_clientdata(client, state);
+ state->clk = pdata->clk;
+ state->tuner = pdata->tuner;
+ state->if_frequency = pdata->if_frequency;
+ state->ts_mode = pdata->ts_mode;
+ state->ts_output_pin = pdata->ts_output_pin;
+ state->spec_inv = pdata->spec_inv;
+ memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version));
+ memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio));
+ INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
+ state->regmap = regmap_init(&client->dev, &regmap_bus, client,
+ &regmap_config);
+ if (IS_ERR(state->regmap)) {
+ ret = PTR_ERR(state->regmap);
+ goto err_kfree;
+ }
+
+ /* Download firmware */
+ if (state->ts_mode != AF9013_TS_MODE_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto err_regmap_exit;
+ }
+
+ /* Firmware version */
+ ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version,
+ sizeof(firmware_version));
+ if (ret)
+ goto err_regmap_exit;
+
+ /* Set GPIOs */
+ for (i = 0; i < sizeof(state->gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->gpio[i]);
+ if (ret)
+ goto err_regmap_exit;
+ }
+
+ /* Create dvb frontend */
+ memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops));
+ if (!pdata->attach_in_use)
+ state->fe.ops.release = NULL;
+ state->fe.demodulator_priv = state;
+
+ /* Setup callbacks */
+ pdata->get_dvb_frontend = af9013_get_dvb_frontend;
+
+ /* Init stats to indicate which stats are supported */
+ c = &state->fe.dtv_property_cache;
+ c->cnr.len = 1;
+
+ dev_info(&client->dev, "Afatech AF9013 successfully attached\n");
+ dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n",
+ firmware_version[0], firmware_version[1],
+ firmware_version[2], firmware_version[3]);
+ return 0;
+err_regmap_exit:
+ regmap_exit(state->regmap);
+err_kfree:
+ kfree(state);
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_remove(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Stop statistics polling */
+ cancel_delayed_work_sync(&state->statistics_work);
+
+ regmap_exit(state->regmap);
+
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id af9013_id_table[] = {
+ {"af9013", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, af9013_id_table);
+
+static struct i2c_driver af9013_driver = {
+ .driver = {
+ .name = "af9013",
+ .suppress_bind_attrs = true,
+ },
+ .probe = af9013_probe,
+ .remove = af9013_remove,
+ .id_table = af9013_id_table,
+};
+
+module_i2c_driver(af9013_driver);
+
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h
index 277112863719..353274524f1b 100644
--- a/drivers/media/dvb-frontends/af9013.h
+++ b/drivers/media/dvb-frontends/af9013.h
@@ -23,29 +23,27 @@
#include <linux/dvb/frontend.h>
-/* AF9013/5 GPIOs (mostly guessed)
- demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
- demod#1-gpio#1 - xtal setting (?)
- demod#1-gpio#3 - tuner#1
- demod#2-gpio#0 - tuner#2
- demod#2-gpio#1 - xtal setting (?)
-*/
-
-struct af9013_config {
- /*
- * I2C address
- */
- u8 i2c_addr;
+/*
+ * I2C address: 0x1c, 0x1d
+ */
+/**
+ * struct af9013_platform_data - Platform data for the af9013 driver
+ * @clk: Clock frequency.
+ * @tuner: Used tuner model.
+ * @if_frequency: IF frequency.
+ * @ts_mode: TS mode.
+ * @ts_output_pin: TS output pin.
+ * @spec_inv: Input spectrum inverted.
+ * @api_version: Firmware API version.
+ * @gpio: GPIOs.
+ * @get_dvb_frontend: Get DVB frontend callback.
+ */
+struct af9013_platform_data {
/*
- * clock
* 20480000, 25000000, 28000000, 28800000
*/
- u32 clock;
-
- /*
- * tuner
- */
+ u32 clk;
#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */
#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */
#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */
@@ -60,33 +58,14 @@ struct af9013_config {
#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */
#define AF9013_TUNER_TDA18218 179 /* NXP */
u8 tuner;
-
- /*
- * IF frequency
- */
u32 if_frequency;
-
- /*
- * TS settings
- */
-#define AF9013_TS_USB 0
-#define AF9013_TS_PARALLEL 1
-#define AF9013_TS_SERIAL 2
- u8 ts_mode:2;
-
- /*
- * input spectrum inversion
- */
+#define AF9013_TS_MODE_USB 0
+#define AF9013_TS_MODE_PARALLEL 1
+#define AF9013_TS_MODE_SERIAL 2
+ u8 ts_mode;
+ u8 ts_output_pin;
bool spec_inv;
-
- /*
- * firmware API version
- */
u8 api_version[4];
-
- /*
- * GPIOs
- */
#define AF9013_GPIO_ON (1 << 0)
#define AF9013_GPIO_EN (1 << 1)
#define AF9013_GPIO_O (1 << 2)
@@ -96,8 +75,29 @@ struct af9013_config {
#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
u8 gpio[4];
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+
+/* private: For legacy media attach wrapper. Do not set value. */
+ bool attach_in_use;
+ u8 i2c_addr;
+ u32 clock;
};
+#define af9013_config af9013_platform_data
+#define AF9013_TS_USB AF9013_TS_MODE_USB
+#define AF9013_TS_PARALLEL AF9013_TS_MODE_PARALLEL
+#define AF9013_TS_SERIAL AF9013_TS_MODE_SERIAL
+
+/*
+ * AF9013/5 GPIOs (mostly guessed)
+ * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ * demod#1-gpio#1 - xtal setting (?)
+ * demod#1-gpio#3 - tuner#1
+ * demod#2-gpio#0 - tuner#2
+ * demod#2-gpio#1 - xtal setting (?)
+ */
+
#if IS_REACHABLE(CONFIG_DVB_AF9013)
extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
struct i2c_adapter *i2c);
diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h
index 31d6538abfae..35ca5c9bcacd 100644
--- a/drivers/media/dvb-frontends/af9013_priv.h
+++ b/drivers/media/dvb-frontends/af9013_priv.h
@@ -24,6 +24,8 @@
#include "dvb_frontend.h"
#include "af9013.h"
#include <linux/firmware.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
#define AF9013_FIRMWARE "dvb-fe-af9013.fw"
diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c
index cf4ac240a01f..6722838c3707 100644
--- a/drivers/media/dvb-frontends/au8522_common.c
+++ b/drivers/media/dvb-frontends/au8522_common.c
@@ -234,6 +234,7 @@ int au8522_init(struct dvb_frontend *fe)
chip, so that when it gets powered back up it won't think
that it is already tuned */
state->current_frequency = 0;
+ state->current_modulation = VSB_8;
au8522_writereg(state, 0xa4, 1 << 5);
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index a2e771305008..343dc92ef54e 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -17,7 +17,6 @@
/* Developer notes:
*
- * VBI support is not yet working
* Enough is implemented here for CVBS and S-Video inputs, but the actual
* analog demodulator code isn't implemented (not needed for xc5000 since it
* has its own demodulator and outputs CVBS)
@@ -179,42 +178,6 @@ static inline struct au8522_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct au8522_state, sd);
}
-static void setup_vbi(struct au8522_state *state, int aud_input)
-{
- int i;
-
- /* These are set to zero regardless of what mode we're in */
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H,
- 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H,
- 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H,
- 0x00);
-
- /* Setup the VBI registers */
- for (i = 0x30; i < 0x60; i++)
- au8522_writereg(state, i, 0x40);
-
- /* For some reason, every register is 0x40 except register 0x44
- (confirmed via the HVR-950q USB capture) */
- au8522_writereg(state, 0x44, 0x60);
-
- /* Enable VBI (we always do this regardless of whether the user is
- viewing closed caption info) */
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H,
- AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON);
-
-}
-
static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo)
{
int i;
@@ -317,8 +280,6 @@ static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo)
AU8522_TOREGAAGC_REG0E5H_CVBS);
au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
- setup_vbi(state, 0);
-
if (is_svideo) {
/* Despite what the table says, for the HVR-950q we still need
to be in CVBS mode for the S-Video input (reason unknown). */
@@ -456,30 +417,29 @@ static void set_audio_input(struct au8522_state *state)
lpfilter_coef[i].reg_val[0]);
}
- /* Setup audio */
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
- au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);
- au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);
- msleep(150);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00);
- msleep(10);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
- msleep(50);
+ /* Set the volume */
au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);
- msleep(80);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
+
+ /* Not sure what this does */
au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
+
+ /* Setup the audio mode to stereo DBX */
au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);
msleep(70);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
+
+ /* Start the audio processing module */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);
+
+ /* Set the audio frequency to 48 KHz */
au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
+
+ /* Set the I2S parameters (WS, LSB, mode, sample rate */
au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);
+
+ /* Enable the I2S output */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
}
/* ----------------------------------------------------------------------- */
@@ -663,10 +623,12 @@ static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
int val = 0;
struct au8522_state *state = to_state(sd);
u8 lock_status;
+ u8 pll_status;
/* Interrogate the decoder to see if we are getting a real signal */
lock_status = au8522_readreg(state, 0x00);
- if (lock_status == 0xa2)
+ pll_status = au8522_readreg(state, 0x7e);
+ if ((lock_status == 0xa2) && (pll_status & 0x10))
vt->signal = 0xffff;
else
vt->signal = 0x00;
diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
index 7ed326e43fc4..3f3635f5a06a 100644
--- a/drivers/media/dvb-frontends/au8522_dig.c
+++ b/drivers/media/dvb-frontends/au8522_dig.c
@@ -271,9 +271,9 @@ static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
return -EINVAL;
}
dprintk("%s() %s MHz\n", __func__, ifmhz);
- au8522_writereg(state, 0x80b5, r0b5);
- au8522_writereg(state, 0x80b6, r0b6);
- au8522_writereg(state, 0x80b7, r0b7);
+ au8522_writereg(state, 0x00b5, r0b5);
+ au8522_writereg(state, 0x00b6, r0b6);
+ au8522_writereg(state, 0x00b7, r0b7);
return 0;
}
@@ -283,33 +283,32 @@ static struct {
u16 reg;
u16 data;
} VSB_mod_tab[] = {
- { 0x8090, 0x84 },
- { 0x4092, 0x11 },
+ { 0x0090, 0x84 },
{ 0x2005, 0x00 },
- { 0x8091, 0x80 },
- { 0x80a3, 0x0c },
- { 0x80a4, 0xe8 },
- { 0x8081, 0xc4 },
- { 0x80a5, 0x40 },
- { 0x80a7, 0x40 },
- { 0x80a6, 0x67 },
- { 0x8262, 0x20 },
- { 0x821c, 0x30 },
- { 0x80d8, 0x1a },
- { 0x8227, 0xa0 },
- { 0x8121, 0xff },
- { 0x80a8, 0xf0 },
- { 0x80a9, 0x05 },
- { 0x80aa, 0x77 },
- { 0x80ab, 0xf0 },
- { 0x80ac, 0x05 },
- { 0x80ad, 0x77 },
- { 0x80ae, 0x41 },
- { 0x80af, 0x66 },
- { 0x821b, 0xcc },
- { 0x821d, 0x80 },
- { 0x80a4, 0xe8 },
- { 0x8231, 0x13 },
+ { 0x0091, 0x80 },
+ { 0x00a3, 0x0c },
+ { 0x00a4, 0xe8 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00a7, 0x40 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00d8, 0x1a },
+ { 0x0227, 0xa0 },
+ { 0x0121, 0xff },
+ { 0x00a8, 0xf0 },
+ { 0x00a9, 0x05 },
+ { 0x00aa, 0x77 },
+ { 0x00ab, 0xf0 },
+ { 0x00ac, 0x05 },
+ { 0x00ad, 0x77 },
+ { 0x00ae, 0x41 },
+ { 0x00af, 0x66 },
+ { 0x021b, 0xcc },
+ { 0x021d, 0x80 },
+ { 0x00a4, 0xe8 },
+ { 0x0231, 0x13 },
};
/* QAM64 Modulation table */
@@ -396,78 +395,78 @@ static struct {
u16 reg;
u16 data;
} QAM256_mod_tab[] = {
- { 0x80a3, 0x09 },
- { 0x80a4, 0x00 },
- { 0x8081, 0xc4 },
- { 0x80a5, 0x40 },
- { 0x80aa, 0x77 },
- { 0x80ad, 0x77 },
- { 0x80a6, 0x67 },
- { 0x8262, 0x20 },
- { 0x821c, 0x30 },
- { 0x80b8, 0x3e },
- { 0x80b9, 0xf0 },
- { 0x80ba, 0x01 },
- { 0x80bb, 0x18 },
- { 0x80bc, 0x50 },
- { 0x80bd, 0x00 },
- { 0x80be, 0xea },
- { 0x80bf, 0xef },
- { 0x80c0, 0xfc },
- { 0x80c1, 0xbd },
- { 0x80c2, 0x1f },
- { 0x80c3, 0xfc },
- { 0x80c4, 0xdd },
- { 0x80c5, 0xaf },
- { 0x80c6, 0x00 },
- { 0x80c7, 0x38 },
- { 0x80c8, 0x30 },
- { 0x80c9, 0x05 },
- { 0x80ca, 0x4a },
- { 0x80cb, 0xd0 },
- { 0x80cc, 0x01 },
- { 0x80cd, 0xd9 },
- { 0x80ce, 0x6f },
- { 0x80cf, 0xf9 },
- { 0x80d0, 0x70 },
- { 0x80d1, 0xdf },
- { 0x80d2, 0xf7 },
- { 0x80d3, 0xc2 },
- { 0x80d4, 0xdf },
- { 0x80d5, 0x02 },
- { 0x80d6, 0x9a },
- { 0x80d7, 0xd0 },
- { 0x8250, 0x0d },
- { 0x8251, 0xcd },
- { 0x8252, 0xe0 },
- { 0x8253, 0x05 },
- { 0x8254, 0xa7 },
- { 0x8255, 0xff },
- { 0x8256, 0xed },
- { 0x8257, 0x5b },
- { 0x8258, 0xae },
- { 0x8259, 0xe6 },
- { 0x825a, 0x3d },
- { 0x825b, 0x0f },
- { 0x825c, 0x0d },
- { 0x825d, 0xea },
- { 0x825e, 0xf2 },
- { 0x825f, 0x51 },
- { 0x8260, 0xf5 },
- { 0x8261, 0x06 },
- { 0x821a, 0x00 },
- { 0x8546, 0x40 },
- { 0x8210, 0x26 },
- { 0x8211, 0xf6 },
- { 0x8212, 0x84 },
- { 0x8213, 0x02 },
- { 0x8502, 0x01 },
- { 0x8121, 0x04 },
- { 0x8122, 0x04 },
- { 0x852e, 0x10 },
- { 0x80a4, 0xca },
- { 0x80a7, 0x40 },
- { 0x8526, 0x01 },
+ { 0x00a3, 0x09 },
+ { 0x00a4, 0x00 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00aa, 0x77 },
+ { 0x00ad, 0x77 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00b8, 0x3e },
+ { 0x00b9, 0xf0 },
+ { 0x00ba, 0x01 },
+ { 0x00bb, 0x18 },
+ { 0x00bc, 0x50 },
+ { 0x00bd, 0x00 },
+ { 0x00be, 0xea },
+ { 0x00bf, 0xef },
+ { 0x00c0, 0xfc },
+ { 0x00c1, 0xbd },
+ { 0x00c2, 0x1f },
+ { 0x00c3, 0xfc },
+ { 0x00c4, 0xdd },
+ { 0x00c5, 0xaf },
+ { 0x00c6, 0x00 },
+ { 0x00c7, 0x38 },
+ { 0x00c8, 0x30 },
+ { 0x00c9, 0x05 },
+ { 0x00ca, 0x4a },
+ { 0x00cb, 0xd0 },
+ { 0x00cc, 0x01 },
+ { 0x00cd, 0xd9 },
+ { 0x00ce, 0x6f },
+ { 0x00cf, 0xf9 },
+ { 0x00d0, 0x70 },
+ { 0x00d1, 0xdf },
+ { 0x00d2, 0xf7 },
+ { 0x00d3, 0xc2 },
+ { 0x00d4, 0xdf },
+ { 0x00d5, 0x02 },
+ { 0x00d6, 0x9a },
+ { 0x00d7, 0xd0 },
+ { 0x0250, 0x0d },
+ { 0x0251, 0xcd },
+ { 0x0252, 0xe0 },
+ { 0x0253, 0x05 },
+ { 0x0254, 0xa7 },
+ { 0x0255, 0xff },
+ { 0x0256, 0xed },
+ { 0x0257, 0x5b },
+ { 0x0258, 0xae },
+ { 0x0259, 0xe6 },
+ { 0x025a, 0x3d },
+ { 0x025b, 0x0f },
+ { 0x025c, 0x0d },
+ { 0x025d, 0xea },
+ { 0x025e, 0xf2 },
+ { 0x025f, 0x51 },
+ { 0x0260, 0xf5 },
+ { 0x0261, 0x06 },
+ { 0x021a, 0x00 },
+ { 0x0546, 0x40 },
+ { 0x0210, 0x26 },
+ { 0x0211, 0xf6 },
+ { 0x0212, 0x84 },
+ { 0x0213, 0x02 },
+ { 0x0502, 0x01 },
+ { 0x0121, 0x04 },
+ { 0x0122, 0x04 },
+ { 0x052e, 0x10 },
+ { 0x00a4, 0xca },
+ { 0x00a7, 0x40 },
+ { 0x0526, 0x01 },
};
static struct {
@@ -654,12 +653,12 @@ static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status)
if (state->current_modulation == VSB_8) {
dprintk("%s() Checking VSB_8\n", __func__);
- reg = au8522_readreg(state, 0x4088);
+ reg = au8522_readreg(state, 0x0088);
if ((reg & 0x03) == 0x03)
*status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
} else {
dprintk("%s() Checking QAM\n", __func__);
- reg = au8522_readreg(state, 0x4541);
+ reg = au8522_readreg(state, 0x0541);
if (reg & 0x80)
*status |= FE_HAS_VITERBI;
if (reg & 0x20)
@@ -745,17 +744,17 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
if (state->current_modulation == QAM_256)
ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
ARRAY_SIZE(qam256_mse2snr_tab),
- au8522_readreg(state, 0x4522),
+ au8522_readreg(state, 0x0522),
snr);
else if (state->current_modulation == QAM_64)
ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
ARRAY_SIZE(qam64_mse2snr_tab),
- au8522_readreg(state, 0x4522),
+ au8522_readreg(state, 0x0522),
snr);
else /* VSB_8 */
ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
ARRAY_SIZE(vsb_mse2snr_tab),
- au8522_readreg(state, 0x4311),
+ au8522_readreg(state, 0x0311),
snr);
if (state->config.led_cfg)
@@ -804,9 +803,9 @@ static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
struct au8522_state *state = fe->demodulator_priv;
if (state->current_modulation == VSB_8)
- *ucblocks = au8522_readreg(state, 0x4087);
+ *ucblocks = au8522_readreg(state, 0x0087);
else
- *ucblocks = au8522_readreg(state, 0x4543);
+ *ucblocks = au8522_readreg(state, 0x0543);
return 0;
}
diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c
index 617c5e29f919..ba63ad170d3c 100644
--- a/drivers/media/dvb-frontends/bcm3510.c
+++ b/drivers/media/dvb-frontends/bcm3510.c
@@ -538,6 +538,7 @@ static int bcm3510_set_frontend(struct dvb_frontend *fe)
cmd.ACQUIRE0.MODE = 0x9;
cmd.ACQUIRE1.SYM_RATE = 0x0;
cmd.ACQUIRE1.IF_FREQ = 0x0;
+ break;
default:
return -EINVAL;
}
@@ -772,7 +773,8 @@ static int bcm3510_init(struct dvb_frontend* fe)
deb_info("attempting to download firmware\n");
if ((ret = bcm3510_init_cold(st)) < 0)
return ret;
- case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */
+ /* fall-through */
+ case JDEC_EEPROM_LOAD_WAIT:
deb_info("firmware is loaded\n");
bcm3510_check_firmware_version(st);
break;
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
index ce37dc2e89c7..12bff778c97f 100644
--- a/drivers/media/dvb-frontends/cxd2841er.c
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -38,6 +38,8 @@
#define MAX_WRITE_REGSIZE 16
#define LOG2_E_100X 144
+#define INTLOG10X100(x) ((u32) (((u64) intlog10(x) * 100) >> 24))
+
/* DVB-C constellation */
enum sony_dvbc_constellation_t {
SONY_DVBC_CONSTELLATION_16QAM,
@@ -65,6 +67,7 @@ struct cxd2841er_priv {
u8 system;
enum cxd2841er_xtal xtal;
enum fe_caps caps;
+ u32 flags;
};
static const struct cxd2841er_cnr_data s_cn_data[] = {
@@ -201,11 +204,6 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = {
{ 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 },
};
-#define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5))
-#define MAKE_IFFREQ_CONFIG_XTAL(xtal, iffreq) ((xtal == SONY_XTAL_24000) ? \
- (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \
- (u32)(((iffreq)/41.0)*16777216.0 + 0.5))
-
static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv);
static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv);
@@ -214,10 +212,8 @@ static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv,
const u8 *data, u32 len)
{
dev_dbg(&priv->i2c->dev,
- "cxd2841er: I2C %s addr %02x reg 0x%02x size %d\n",
- (write == 0 ? "read" : "write"), addr, reg, len);
- print_hex_dump_bytes("cxd2841er: I2C data: ",
- DUMP_PREFIX_OFFSET, data, len);
+ "cxd2841er: I2C %s addr %02x reg 0x%02x size %d data %*ph\n",
+ (write == 0 ? "read" : "write"), addr, reg, len, len, data);
}
static int cxd2841er_write_regs(struct cxd2841er_priv *priv,
@@ -284,17 +280,8 @@ static int cxd2841er_read_regs(struct cxd2841er_priv *priv,
}
};
- ret = i2c_transfer(priv->i2c, &msg[0], 1);
- if (ret >= 0 && ret != 1)
- ret = -EIO;
- if (ret < 0) {
- dev_warn(&priv->i2c->dev,
- "%s: i2c rw failed=%d addr=%02x reg=%02x\n",
- KBUILD_MODNAME, ret, i2c_addr, reg);
- return ret;
- }
- ret = i2c_transfer(priv->i2c, &msg[1], 1);
- if (ret >= 0 && ret != 1)
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret >= 0 && ret != 2)
ret = -EIO;
if (ret < 0) {
dev_warn(&priv->i2c->dev,
@@ -327,6 +314,49 @@ static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv,
return cxd2841er_write_reg(priv, addr, reg, data);
}
+static u32 cxd2841er_calc_iffreq_xtal(enum cxd2841er_xtal xtal, u32 ifhz)
+{
+ u64 tmp;
+
+ tmp = (u64) ifhz * 16777216;
+ do_div(tmp, ((xtal == SONY_XTAL_24000) ? 48000000 : 41000000));
+
+ return (u32) tmp;
+}
+
+static u32 cxd2841er_calc_iffreq(u32 ifhz)
+{
+ return cxd2841er_calc_iffreq_xtal(SONY_XTAL_20500, ifhz);
+}
+
+static int cxd2841er_get_if_hz(struct cxd2841er_priv *priv, u32 def_hz)
+{
+ u32 hz;
+
+ if (priv->frontend.ops.tuner_ops.get_if_frequency
+ && (priv->flags & CXD2841ER_AUTO_IFHZ))
+ priv->frontend.ops.tuner_ops.get_if_frequency(
+ &priv->frontend, &hz);
+ else
+ hz = def_hz;
+
+ return hz;
+}
+
+static int cxd2841er_tuner_set(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+ if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv,
u32 symbol_rate)
{
@@ -884,6 +914,18 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
/*
* slave Bank Addr Bit default Name
+ * <SLV-T> 00h C4h [1:0] 2'b?? OSERCKMODE
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
+ /*
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h D1h [1:0] 2'b?? OSERDUTYMODE
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd1,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
+ /*
+ * slave Bank Addr Bit default Name
* <SLV-T> 00h D9h [7:0] 8'h08 OTSCKPERIOD
*/
cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08);
@@ -897,7 +939,8 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
* slave Bank Addr Bit default Name
* <SLV-T> 00h 33h [1:0] 2'b01 OREG_CKSEL_TSIF
*/
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, 0x00, 0x03);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
/*
* Enable TS IF Clock
* slave Bank Addr Bit default Name
@@ -1421,11 +1464,11 @@ static int cxd2841er_read_ber_i(struct cxd2841er_priv *priv,
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
cxd2841er_read_regs(priv, I2C_SLVT, 0x5B, pktnum, sizeof(pktnum));
cxd2841er_read_regs(priv, I2C_SLVT, 0x16, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
if (!pktnum[0] && !pktnum[1]) {
dev_dbg(&priv->i2c->dev,
"%s(): no valid BER data\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return -EINVAL;
}
@@ -1435,7 +1478,6 @@ static int cxd2841er_read_ber_i(struct cxd2841er_priv *priv,
dev_dbg(&priv->i2c->dev, "%s(): bit_error=%u bit_count=%u\n",
__func__, *bit_error, *bit_count);
- cxd2841er_unfreeze_regs(priv);
return 0;
}
@@ -1645,6 +1687,8 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv,
* <SLV-T> A1h 12h [7:0] ICPM_QUICKCNDT[7:0]
*/
cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3);
+ cxd2841er_unfreeze_regs(priv);
+
if (data[0] & 0x01) {
value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF);
min_index = 0;
@@ -1687,11 +1731,9 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv,
} else {
dev_dbg(&priv->i2c->dev,
"%s(): no data available\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return -EINVAL;
}
done:
- cxd2841er_unfreeze_regs(priv);
*snr = res;
return 0;
}
@@ -1720,12 +1762,12 @@ static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr)
cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1);
qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07);
cxd2841er_read_regs(priv, I2C_SLVT, 0x4C, data, 2);
+ cxd2841er_unfreeze_regs(priv);
reg = ((u32)(data[0]&0x1f) << 8) | (u32)data[1];
if (reg == 0) {
dev_dbg(&priv->i2c->dev,
"%s(): reg value out of range\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return 0;
}
@@ -1746,11 +1788,9 @@ static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr)
*snr = -88 * (int32_t)sony_log(reg) + 86999;
break;
default:
- cxd2841er_unfreeze_regs(priv);
return -EINVAL;
}
- cxd2841er_unfreeze_regs(priv);
return 0;
}
@@ -1769,17 +1809,17 @@ static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr)
cxd2841er_freeze_regs(priv);
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
reg = ((u32)data[0] << 8) | (u32)data[1];
if (reg == 0) {
dev_dbg(&priv->i2c->dev,
"%s(): reg value out of range\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return 0;
}
if (reg > 4996)
reg = 4996;
- *snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500;
- cxd2841er_unfreeze_regs(priv);
+ *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(5350 - reg)) + 285);
return 0;
}
@@ -1798,18 +1838,17 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr)
cxd2841er_freeze_regs(priv);
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
reg = ((u32)data[0] << 8) | (u32)data[1];
if (reg == 0) {
dev_dbg(&priv->i2c->dev,
"%s(): reg value out of range\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return 0;
}
if (reg > 10876)
reg = 10876;
- *snr = 10000 * ((intlog10(reg) -
- intlog10(12600 - reg)) >> 24) + 32000;
- cxd2841er_unfreeze_regs(priv);
+ *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(12600 - reg)) + 320);
return 0;
}
@@ -1829,15 +1868,15 @@ static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr)
cxd2841er_freeze_regs(priv);
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
reg = ((u32)data[0] << 8) | (u32)data[1];
if (reg == 0) {
dev_dbg(&priv->i2c->dev,
"%s(): reg value out of range\n", __func__);
- cxd2841er_unfreeze_regs(priv);
return 0;
}
*snr = 10000 * (intlog10(reg) >> 24) - 9031;
- cxd2841er_unfreeze_regs(priv);
return 0;
}
@@ -2136,7 +2175,7 @@ static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv,
static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
u32 bandwidth)
{
- u32 iffreq;
+ u32 iffreq, ifhz;
u8 data[MAX_WRITE_REGSIZE];
const uint8_t nominalRate8bw[3][5] = {
@@ -2239,10 +2278,12 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef8bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80);
+ ifhz = cxd2841er_get_if_hz(priv, 4800000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2267,10 +2308,12 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef7bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20);
+ ifhz = cxd2841er_get_if_hz(priv, 4200000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2295,10 +2338,12 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef6bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2323,10 +2368,12 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef5bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2351,10 +2398,12 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef17bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.50);
+ ifhz = cxd2841er_get_if_hz(priv, 3500000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2373,7 +2422,7 @@ static int cxd2841er_sleep_tc_to_active_t_band(
struct cxd2841er_priv *priv, u32 bandwidth)
{
u8 data[MAX_WRITE_REGSIZE];
- u32 iffreq;
+ u32 iffreq, ifhz;
u8 nominalRate8bw[3][5] = {
/* TRCG Nominal Rate [37:0] */
{0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
@@ -2450,10 +2499,12 @@ static int cxd2841er_sleep_tc_to_active_t_band(
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef8bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80);
+ ifhz = cxd2841er_get_if_hz(priv, 4800000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2485,10 +2536,12 @@ static int cxd2841er_sleep_tc_to_active_t_band(
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef7bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20);
+ ifhz = cxd2841er_get_if_hz(priv, 4200000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2520,10 +2573,12 @@ static int cxd2841er_sleep_tc_to_active_t_band(
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef6bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2555,10 +2610,12 @@ static int cxd2841er_sleep_tc_to_active_t_band(
/* Group delay equaliser settings for
* ASCOT2D, ASCOT2E and ASCOT3 tuners
*/
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef5bw[priv->xtal], 14);
/* <IF freq setting> */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2591,7 +2648,7 @@ static int cxd2841er_sleep_tc_to_active_t_band(
static int cxd2841er_sleep_tc_to_active_i_band(
struct cxd2841er_priv *priv, u32 bandwidth)
{
- u32 iffreq;
+ u32 iffreq, ifhz;
u8 data[3];
/* TRCG Nominal Rate */
@@ -2656,11 +2713,13 @@ static int cxd2841er_sleep_tc_to_active_i_band(
cxd2841er_write_regs(priv, I2C_SLVT,
0x9F, nominalRate8bw[priv->xtal], 5);
/* Group delay equaliser settings for ASCOT tuners optimized */
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef8bw[priv->xtal], 14);
/* IF freq setting */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.75);
+ ifhz = cxd2841er_get_if_hz(priv, 4750000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2685,11 +2744,13 @@ static int cxd2841er_sleep_tc_to_active_i_band(
cxd2841er_write_regs(priv, I2C_SLVT,
0x9F, nominalRate7bw[priv->xtal], 5);
/* Group delay equaliser settings for ASCOT tuners optimized */
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef7bw[priv->xtal], 14);
/* IF freq setting */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.15);
+ ifhz = cxd2841er_get_if_hz(priv, 4150000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2714,11 +2775,13 @@ static int cxd2841er_sleep_tc_to_active_i_band(
cxd2841er_write_regs(priv, I2C_SLVT,
0x9F, nominalRate6bw[priv->xtal], 5);
/* Group delay equaliser settings for ASCOT tuners optimized */
- cxd2841er_write_regs(priv, I2C_SLVT,
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
0xA6, itbCoef6bw[priv->xtal], 14);
/* IF freq setting */
- iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.55);
+ ifhz = cxd2841er_get_if_hz(priv, 3550000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
data[0] = (u8) ((iffreq >> 16) & 0xff);
data[1] = (u8)((iffreq >> 8) & 0xff);
data[2] = (u8)(iffreq & 0xff);
@@ -2761,7 +2824,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
u8 b10_b6[3];
- u32 iffreq;
+ u32 iffreq, ifhz;
if (bandwidth != 6000000 &&
bandwidth != 7000000 &&
@@ -2776,16 +2839,20 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
switch (bandwidth) {
case 8000000:
case 7000000:
- cxd2841er_write_regs(
- priv, I2C_SLVT, 0xa6,
- bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6));
- iffreq = MAKE_IFFREQ_CONFIG(4.9);
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(
+ priv, I2C_SLVT, 0xa6,
+ bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6));
+ ifhz = cxd2841er_get_if_hz(priv, 4900000);
+ iffreq = cxd2841er_calc_iffreq(ifhz);
break;
case 6000000:
- cxd2841er_write_regs(
- priv, I2C_SLVT, 0xa6,
- bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6));
- iffreq = MAKE_IFFREQ_CONFIG(3.7);
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(
+ priv, I2C_SLVT, 0xa6,
+ bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6));
+ ifhz = cxd2841er_get_if_hz(priv, 3700000);
+ iffreq = cxd2841er_calc_iffreq(ifhz);
break;
default:
dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
@@ -2872,8 +2939,9 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
/* Set SLV-T Bank : 0x10 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
- /* ASCOT setting ON */
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
/* Set SLV-T Bank : 0x18 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
/* Pre-RS BER moniter setting */
@@ -2950,8 +3018,9 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
/* Set SLV-T Bank : 0x10 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
- /* ASCOT setting ON */
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
/* Set SLV-T Bank : 0x20 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
/* Acquisition optimization setting */
@@ -3088,8 +3157,9 @@ static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv,
cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
/* Enable ADC 4 */
cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
- /* ASCOT setting ON */
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
/* FEC Auto Recovery setting */
cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01);
@@ -3173,8 +3243,9 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48);
/* Set SLV-T Bank : 0x10 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
- /* ASCOT setting ON */
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
/* Set SLV-T Bank : 0x40 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
/* Demod setting */
@@ -3208,7 +3279,10 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe,
else if (priv->state == STATE_ACTIVE_TC)
cxd2841er_read_status_tc(fe, &status);
- cxd2841er_read_signal_strength(fe);
+ if (priv->state == STATE_ACTIVE_TC || priv->state == STATE_ACTIVE_S)
+ cxd2841er_read_signal_strength(fe);
+ else
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
if (status & FE_HAS_LOCK) {
cxd2841er_read_snr(fe);
@@ -3236,6 +3310,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
__func__,
(p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
p->frequency, symbol_rate, priv->xtal);
+
+ if (priv->flags & CXD2841ER_EARLY_TUNE)
+ cxd2841er_tuner_set(fe);
+
switch (priv->state) {
case STATE_SLEEP_S:
ret = cxd2841er_sleep_s_to_active_s(
@@ -3254,12 +3332,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__);
goto done;
}
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (!(priv->flags & CXD2841ER_EARLY_TUNE))
+ cxd2841er_tuner_set(fe);
+
cxd2841er_tune_done(priv);
timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150;
for (i = 0; i < timeout / CXD2841ER_DVBS_POLLING_INVL; i++) {
@@ -3298,6 +3374,10 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n",
__func__, p->delivery_system, p->bandwidth_hz);
+
+ if (priv->flags & CXD2841ER_EARLY_TUNE)
+ cxd2841er_tuner_set(fe);
+
if (p->delivery_system == SYS_DVBT) {
priv->system = SYS_DVBT;
switch (priv->state) {
@@ -3379,13 +3459,15 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
}
if (ret)
goto done;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (!(priv->flags & CXD2841ER_EARLY_TUNE))
+ cxd2841er_tuner_set(fe);
+
cxd2841er_tune_done(priv);
+
+ if (priv->flags & CXD2841ER_NO_WAIT_LOCK)
+ goto done;
+
timeout = 2500;
while (timeout > 0) {
ret = cxd2841er_read_status_tc(fe, &status);
@@ -3705,14 +3787,20 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe)
dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n",
__func__, p->bandwidth_hz);
cxd2841er_shutdown_to_sleep_tc(priv);
- /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */
+ /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 (0 for NO_AGCNEG */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, 0x40, 0x40);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb,
+ ((priv->flags & CXD2841ER_NO_AGCNEG) ? 0x00 : 0x40), 0x40);
/* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */
cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50);
/* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
- cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x80 : 0x00), 0x80);
+
+ /* clear TSCFG bits 3+4 */
+ if (priv->flags & CXD2841ER_TSBITS)
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x18);
cxd2841er_init_stats(fe);
@@ -3740,6 +3828,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1;
priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1;
priv->xtal = cfg->xtal;
+ priv->flags = cfg->flags;
priv->frontend.demodulator_priv = priv;
dev_info(&priv->i2c->dev,
"%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n",
@@ -3747,16 +3836,39 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
priv->i2c_addr_slvx, priv->i2c_addr_slvt);
chip_id = cxd2841er_chip_id(priv);
switch (chip_id) {
+ case CXD2837ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2837ER DVB-T/T2/C demodulator");
+ name = "CXD2837ER";
+ type = "C/T/T2";
+ break;
+ case CXD2838ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2838ER ISDB-T demodulator");
+ cxd2841er_t_c_ops.delsys[0] = SYS_ISDBT;
+ cxd2841er_t_c_ops.delsys[1] = SYS_UNDEFINED;
+ cxd2841er_t_c_ops.delsys[2] = SYS_UNDEFINED;
+ name = "CXD2838ER";
+ type = "ISDB-T";
+ break;
case CXD2841ER_CHIP_ID:
snprintf(cxd2841er_t_c_ops.info.name, 128,
"Sony CXD2841ER DVB-T/T2/C demodulator");
name = "CXD2841ER";
+ type = "T/T2/C/ISDB-T";
+ break;
+ case CXD2843ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2843ER DVB-T/T2/C/C2 demodulator");
+ name = "CXD2843ER";
+ type = "C/C2/T/T2";
break;
case CXD2854ER_CHIP_ID:
snprintf(cxd2841er_t_c_ops.info.name, 128,
"Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator");
cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT;
name = "CXD2854ER";
+ type = "C/C2/T/T2/ISDB-T";
break;
default:
dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n",
@@ -3776,7 +3888,6 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
memcpy(&priv->frontend.ops,
&cxd2841er_t_c_ops,
sizeof(struct dvb_frontend_ops));
- type = "T/T2/C/ISDB-T";
}
dev_info(&priv->i2c->dev,
diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h
index 7f1acfb8f4f5..dc32f5fb6662 100644
--- a/drivers/media/dvb-frontends/cxd2841er.h
+++ b/drivers/media/dvb-frontends/cxd2841er.h
@@ -24,6 +24,15 @@
#include <linux/dvb/frontend.h>
+#define CXD2841ER_USE_GATECTRL 1 /* bit 0 */
+#define CXD2841ER_AUTO_IFHZ 2 /* bit 1 */
+#define CXD2841ER_TS_SERIAL 4 /* bit 2 */
+#define CXD2841ER_ASCOT 8 /* bit 3 */
+#define CXD2841ER_EARLY_TUNE 16 /* bit 4 */
+#define CXD2841ER_NO_WAIT_LOCK 32 /* bit 5 */
+#define CXD2841ER_NO_AGCNEG 64 /* bit 6 */
+#define CXD2841ER_TSBITS 128 /* bit 7 */
+
enum cxd2841er_xtal {
SONY_XTAL_20500, /* 20.5 MHz */
SONY_XTAL_24000, /* 24 MHz */
@@ -33,6 +42,7 @@ enum cxd2841er_xtal {
struct cxd2841er_config {
u8 i2c_addr;
enum cxd2841er_xtal xtal;
+ u32 flags;
};
#if IS_REACHABLE(CONFIG_DVB_CXD2841ER)
diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h
index 0bbce451149f..6a7126480889 100644
--- a/drivers/media/dvb-frontends/cxd2841er_priv.h
+++ b/drivers/media/dvb-frontends/cxd2841er_priv.h
@@ -25,7 +25,10 @@
#define I2C_SLVX 0
#define I2C_SLVT 1
+#define CXD2837ER_CHIP_ID 0xb1
+#define CXD2838ER_CHIP_ID 0xb0
#define CXD2841ER_CHIP_ID 0xa7
+#define CXD2843ER_CHIP_ID 0xa4
#define CXD2854ER_CHIP_ID 0xc1
#define CXD2841ER_DVBS_POLLING_INVL 10
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 3815ea515364..1caa04d8f60f 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -279,10 +279,10 @@ static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_p
if (state->version != SOC7090)
reg_1280 &= ~((1 << 11));
reg_1280 &= ~(1 << 6);
- /* fall through wanted to enable the interfaces */
-
+ /* fall-through */
+ case DIB7000P_POWER_INTERFACE_ONLY:
/* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
- case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ /* TODO power up either SDIO or I2C */
if (state->version == SOC7090)
reg_1280 &= ~((1 << 7) | (1 << 5));
else
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
index 4442e478db72..cd69e187ba7a 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
@@ -307,7 +307,7 @@ int drxbsp_tuner_default_i2c_write_read(struct tuner_instance *tuner,
* \def DRX_UNKNOWN
* \brief Generic UNKNOWN value for DRX enumerated types.
*
-* Used to indicate that the parameter value is unknown or not yet initalized.
+* Used to indicate that the parameter value is unknown or not yet initialized.
*/
#ifndef DRX_UNKNOWN
#define DRX_UNKNOWN (254)
@@ -450,19 +450,6 @@ MACROS
((u8)((((u16)x)>>8)&0xFF))
/**
-* \brief Macro to sign extend signed 9 bit value to signed 16 bit value
-*/
-#define DRX_S9TOS16(x) ((((u16)x)&0x100) ? ((s16)((u16)(x)|0xFF00)) : (x))
-
-/**
-* \brief Macro to sign extend signed 9 bit value to signed 16 bit value
-*/
-#define DRX_S24TODRXFREQ(x) ((((u32) x) & 0x00800000UL) ? \
- ((s32) \
- (((u32) x) | 0xFF000000)) : \
- ((s32) x))
-
-/**
* \brief Macro to convert 16 bit register value to a s32
*/
#define DRX_U16TODRXFREQ(x) ((x & 0x8000) ? \
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index daeaf965dd56..14040c915dbb 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -2837,7 +2837,8 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o
/* coef = 188/204 */
max_bit_rate =
(ext_attr->curr_symbol_rate / 8) * nr_bits * 188;
- /* pass through b/c Annex A/c need following settings */
+ /* pass through as b/c Annex A/c need following settings */
+ /* fall-through */
case DRX_STANDARD_ITU_B:
rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0);
if (rc != 0) {
@@ -4776,9 +4777,9 @@ set_frequency(struct drx_demod_instance *demod,
No need to account for mirroring on RF
*/
switch (ext_attr->standard) {
- case DRX_STANDARD_ITU_A: /* fallthrough */
- case DRX_STANDARD_ITU_C: /* fallthrough */
- case DRX_STANDARD_PAL_SECAM_LP: /* fallthrough */
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ case DRX_STANDARD_PAL_SECAM_LP:
case DRX_STANDARD_8VSB:
select_pos_image = true;
break;
@@ -4787,11 +4788,12 @@ set_frequency(struct drx_demod_instance *demod,
Sound carrier is already 3Mhz above centre frequency due
to tuner setting so now add an extra shift of 1MHz... */
fm_frequency_shift = 1000;
- case DRX_STANDARD_ITU_B: /* fallthrough */
- case DRX_STANDARD_NTSC: /* fallthrough */
- case DRX_STANDARD_PAL_SECAM_BG: /* fallthrough */
- case DRX_STANDARD_PAL_SECAM_DK: /* fallthrough */
- case DRX_STANDARD_PAL_SECAM_I: /* fallthrough */
+ /*fall through */
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_NTSC:
+ case DRX_STANDARD_PAL_SECAM_BG:
+ case DRX_STANDARD_PAL_SECAM_DK:
+ case DRX_STANDARD_PAL_SECAM_I:
case DRX_STANDARD_PAL_SECAM_L:
select_pos_image = false;
break;
diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index 71910561005f..17638e08835a 100644
--- a/drivers/media/dvb-frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
@@ -1517,12 +1517,14 @@ static int SetDeviceTypeId(struct drxd_state *state)
switch (deviceId) {
case 4:
state->diversity = 1;
+ /* fall through */
case 3:
case 7:
state->PGA = 1;
break;
case 6:
state->diversity = 1;
+ /* fall through */
case 5:
case 8:
break;
@@ -1969,7 +1971,8 @@ static int DRX_Start(struct drxd_state *state, s32 off)
switch (p->transmission_mode) {
default: /* Not set, detect it automatically */
operationMode |= SC_RA_RAM_OP_AUTO_MODE__M;
- /* fall through , try first guess DRX_FFTMODE_8K */
+ /* try first guess DRX_FFTMODE_8K */
+ /* fall through */
case TRANSMISSION_MODE_8K:
transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K;
if (state->type_A) {
@@ -2143,8 +2146,8 @@ static int DRX_Start(struct drxd_state *state, s32 off)
switch (p->modulation) {
default:
operationMode |= SC_RA_RAM_OP_AUTO_CONST__M;
- /* fall through , try first guess
- DRX_CONSTELLATION_QAM64 */
+ /* try first guess DRX_CONSTELLATION_QAM64 */
+ /* fall through */
case QAM_64:
transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64;
if (state->type_A) {
@@ -2280,6 +2283,7 @@ static int DRX_Start(struct drxd_state *state, s32 off)
break;
default:
operationMode |= SC_RA_RAM_OP_AUTO_RATE__M;
+ /* fall through */
case FEC_2_3:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3;
if (state->type_A) {
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 050fe34342d3..48a8aad47a74 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -3271,10 +3271,12 @@ static int dvbt_sc_command(struct drxk_state *state,
case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
/* All commands using 1 parameters */
+ /* fall through */
case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
case OFDM_SC_RA_RAM_CMD_USER_IO:
status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
/* All commands using 0 parameters */
+ /* fall through */
case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
case OFDM_SC_RA_RAM_CMD_NULL:
/* Write command */
@@ -3782,7 +3784,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
case TRANSMISSION_MODE_AUTO:
default:
operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
- /* fall through , try first guess DRX_FFTMODE_8K */
+ /* try first guess DRX_FFTMODE_8K */
+ /* fall through */
case TRANSMISSION_MODE_8K:
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
break;
@@ -3796,7 +3799,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
default:
case GUARD_INTERVAL_AUTO:
operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
- /* fall through , try first guess DRX_GUARD_1DIV4 */
+ /* try first guess DRX_GUARD_1DIV4 */
+ /* fall through */
case GUARD_INTERVAL_1_4:
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
break;
@@ -3817,9 +3821,9 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
case HIERARCHY_NONE:
default:
operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
- /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
+ /* try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
/* transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
- /* break; */
+ /* fall through */
case HIERARCHY_1:
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
break;
@@ -3837,7 +3841,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
case QAM_AUTO:
default:
operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
- /* fall through , try first guess DRX_CONSTELLATION_QAM64 */
+ /* try first guess DRX_CONSTELLATION_QAM64 */
+ /* fall through */
case QAM_64:
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
break;
@@ -3880,7 +3885,8 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
case FEC_AUTO:
default:
operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
- /* fall through , try first guess DRX_CODERATE_2DIV3 */
+ /* try first guess DRX_CODERATE_2DIV3 */
+ /* fall through */
case FEC_2_3:
transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
break;
@@ -3914,7 +3920,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
switch (state->props.bandwidth_hz) {
case 0:
state->props.bandwidth_hz = 8000000;
- /* fall though */
+ /* fall through */
case 8000000:
bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c
index ef3021e964be..cb486e879fdd 100644
--- a/drivers/media/dvb-frontends/lnbh25.c
+++ b/drivers/media/dvb-frontends/lnbh25.c
@@ -76,8 +76,8 @@ static int lnbh25_read_vmon(struct lnbh25_priv *priv)
return ret;
}
}
- print_hex_dump_bytes("lnbh25_read_vmon: ",
- DUMP_PREFIX_OFFSET, status, sizeof(status));
+ dev_dbg(&priv->i2c->dev, "%s(): %*ph\n",
+ __func__, (int) sizeof(status), status);
if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) {
dev_err(&priv->i2c->dev,
"%s(): voltage in failure state, status reg 0x%x\n",
@@ -178,7 +178,7 @@ struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe,
fe->ops.release_sec = lnbh25_release;
fe->ops.set_voltage = lnbh25_set_voltage;
- dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
+ dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
__func__, priv->i2c_address);
return fe;
}
diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c
index e127090f2d22..d5fa96f0a6cd 100644
--- a/drivers/media/dvb-frontends/mt352.c
+++ b/drivers/media/dvb-frontends/mt352.c
@@ -211,6 +211,7 @@ static int mt352_set_parameters(struct dvb_frontend *fe)
if (op->hierarchy == HIERARCHY_AUTO ||
op->hierarchy == HIERARCHY_NONE)
break;
+ /* fall through */
default:
return -EINVAL;
}
diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c
index 62aa00767015..5f2549c48eb0 100644
--- a/drivers/media/dvb-frontends/or51132.c
+++ b/drivers/media/dvb-frontends/or51132.c
@@ -493,8 +493,8 @@ start:
switch (reg&0xff) {
case 0x06:
if (reg & 0x1000) usK = 3 << 24;
- /* Fall through to QAM64 case */
- case 0x43:
+ /* fall through */
+ case 0x43: /* QAM64 */
c = 150204167;
break;
case 0x45:
diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index f29750a96196..dd09336a135b 100644
--- a/drivers/media/dvb-frontends/s5h1411.c
+++ b/drivers/media/dvb-frontends/s5h1411.c
@@ -51,7 +51,7 @@ static int debug;
#define dprintk(arg...) do { \
if (debug) \
printk(arg); \
- } while (0)
+} while (0)
/* Register values to initialise the demod, defaults to VSB */
static struct init_tab {
@@ -410,7 +410,7 @@ static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz)
default:
dprintk("%s(%d KHz) Invalid, defaulting to 5380\n",
__func__, KHz);
- /* no break, need to continue */
+ /* fall through */
case 5380:
case 44000:
s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4);
diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index fd49c436a36d..8ac0f598978d 100644
--- a/drivers/media/dvb-frontends/stv0367.c
+++ b/drivers/media/dvb-frontends/stv0367.c
@@ -25,7 +25,10 @@
#include <linux/slab.h>
#include <linux/i2c.h>
+#include "dvb_math.h"
+
#include "stv0367.h"
+#include "stv0367_defs.h"
#include "stv0367_regs.h"
#include "stv0367_priv.h"
@@ -45,6 +48,8 @@ module_param_named(i2c_debug, i2cdebug, int, 0644);
} while (0)
/* DVB-C */
+enum active_demod_state { demod_none, demod_ter, demod_cab };
+
struct stv0367cab_state {
enum stv0367_cab_signal_type state;
u32 mclk;
@@ -56,6 +61,7 @@ struct stv0367cab_state {
u32 freq_khz; /* found frequency (in kHz) */
u32 symbol_rate; /* found symbol rate (in Bds) */
enum fe_spectral_inversion spect_inv; /* Spectrum Inversion */
+ u32 qamfec_status_reg; /* status reg to poll for FEC Lock */
};
struct stv0367ter_state {
@@ -89,461 +95,12 @@ struct stv0367_state {
struct stv0367cab_state *cab_state;
/* DVB-T */
struct stv0367ter_state *ter_state;
-};
-
-struct st_register {
- u16 addr;
- u8 value;
-};
-
-/* values for STV4100 XTAL=30M int clk=53.125M*/
-static struct st_register def0367ter[STV0367TER_NBREGS] = {
- {R367TER_ID, 0x60},
- {R367TER_I2CRPT, 0xa0},
- /* {R367TER_I2CRPT, 0x22},*/
- {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */
- {R367TER_IOCFG0, 0x40},
- {R367TER_DAC0R, 0x00},
- {R367TER_IOCFG1, 0x00},
- {R367TER_DAC1R, 0x00},
- {R367TER_IOCFG2, 0x62},
- {R367TER_SDFR, 0x00},
- {R367TER_STATUS, 0xf8},
- {R367TER_AUX_CLK, 0x0a},
- {R367TER_FREESYS1, 0x00},
- {R367TER_FREESYS2, 0x00},
- {R367TER_FREESYS3, 0x00},
- {R367TER_GPIO_CFG, 0x55},
- {R367TER_GPIO_CMD, 0x00},
- {R367TER_AGC2MAX, 0xff},
- {R367TER_AGC2MIN, 0x00},
- {R367TER_AGC1MAX, 0xff},
- {R367TER_AGC1MIN, 0x00},
- {R367TER_AGCR, 0xbc},
- {R367TER_AGC2TH, 0x00},
- {R367TER_AGC12C, 0x00},
- {R367TER_AGCCTRL1, 0x85},
- {R367TER_AGCCTRL2, 0x1f},
- {R367TER_AGC1VAL1, 0x00},
- {R367TER_AGC1VAL2, 0x00},
- {R367TER_AGC2VAL1, 0x6f},
- {R367TER_AGC2VAL2, 0x05},
- {R367TER_AGC2PGA, 0x00},
- {R367TER_OVF_RATE1, 0x00},
- {R367TER_OVF_RATE2, 0x00},
- {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */
- {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */
- {R367TER_INC_DEROT1, 0x55},
- {R367TER_INC_DEROT2, 0x55},
- {R367TER_PPM_CPAMP_DIR, 0x2c},
- {R367TER_PPM_CPAMP_INV, 0x00},
- {R367TER_FREESTFE_1, 0x00},
- {R367TER_FREESTFE_2, 0x1c},
- {R367TER_DCOFFSET, 0x00},
- {R367TER_EN_PROCESS, 0x05},
- {R367TER_SDI_SMOOTHER, 0x80},
- {R367TER_FE_LOOP_OPEN, 0x1c},
- {R367TER_FREQOFF1, 0x00},
- {R367TER_FREQOFF2, 0x00},
- {R367TER_FREQOFF3, 0x00},
- {R367TER_TIMOFF1, 0x00},
- {R367TER_TIMOFF2, 0x00},
- {R367TER_EPQ, 0x02},
- {R367TER_EPQAUTO, 0x01},
- {R367TER_SYR_UPDATE, 0xf5},
- {R367TER_CHPFREE, 0x00},
- {R367TER_PPM_STATE_MAC, 0x23},
- {R367TER_INR_THRESHOLD, 0xff},
- {R367TER_EPQ_TPS_ID_CELL, 0xf9},
- {R367TER_EPQ_CFG, 0x00},
- {R367TER_EPQ_STATUS, 0x01},
- {R367TER_AUTORELOCK, 0x81},
- {R367TER_BER_THR_VMSB, 0x00},
- {R367TER_BER_THR_MSB, 0x00},
- {R367TER_BER_THR_LSB, 0x00},
- {R367TER_CCD, 0x83},
- {R367TER_SPECTR_CFG, 0x00},
- {R367TER_CHC_DUMMY, 0x18},
- {R367TER_INC_CTL, 0x88},
- {R367TER_INCTHRES_COR1, 0xb4},
- {R367TER_INCTHRES_COR2, 0x96},
- {R367TER_INCTHRES_DET1, 0x0e},
- {R367TER_INCTHRES_DET2, 0x11},
- {R367TER_IIR_CELLNB, 0x8d},
- {R367TER_IIRCX_COEFF1_MSB, 0x00},
- {R367TER_IIRCX_COEFF1_LSB, 0x00},
- {R367TER_IIRCX_COEFF2_MSB, 0x09},
- {R367TER_IIRCX_COEFF2_LSB, 0x18},
- {R367TER_IIRCX_COEFF3_MSB, 0x14},
- {R367TER_IIRCX_COEFF3_LSB, 0x9c},
- {R367TER_IIRCX_COEFF4_MSB, 0x00},
- {R367TER_IIRCX_COEFF4_LSB, 0x00},
- {R367TER_IIRCX_COEFF5_MSB, 0x36},
- {R367TER_IIRCX_COEFF5_LSB, 0x42},
- {R367TER_FEPATH_CFG, 0x00},
- {R367TER_PMC1_FUNC, 0x65},
- {R367TER_PMC1_FOR, 0x00},
- {R367TER_PMC2_FUNC, 0x00},
- {R367TER_STATUS_ERR_DA, 0xe0},
- {R367TER_DIG_AGC_R, 0xfe},
- {R367TER_COMAGC_TARMSB, 0x0b},
- {R367TER_COM_AGC_TAR_ENMODE, 0x41},
- {R367TER_COM_AGC_CFG, 0x3e},
- {R367TER_COM_AGC_GAIN1, 0x39},
- {R367TER_AUT_AGC_TARGETMSB, 0x0b},
- {R367TER_LOCK_DET_MSB, 0x01},
- {R367TER_AGCTAR_LOCK_LSBS, 0x40},
- {R367TER_AUT_GAIN_EN, 0xf4},
- {R367TER_AUT_CFG, 0xf0},
- {R367TER_LOCKN, 0x23},
- {R367TER_INT_X_3, 0x00},
- {R367TER_INT_X_2, 0x03},
- {R367TER_INT_X_1, 0x8d},
- {R367TER_INT_X_0, 0xa0},
- {R367TER_MIN_ERRX_MSB, 0x00},
- {R367TER_COR_CTL, 0x23},
- {R367TER_COR_STAT, 0xf6},
- {R367TER_COR_INTEN, 0x00},
- {R367TER_COR_INTSTAT, 0x3f},
- {R367TER_COR_MODEGUARD, 0x03},
- {R367TER_AGC_CTL, 0x08},
- {R367TER_AGC_MANUAL1, 0x00},
- {R367TER_AGC_MANUAL2, 0x00},
- {R367TER_AGC_TARG, 0x16},
- {R367TER_AGC_GAIN1, 0x53},
- {R367TER_AGC_GAIN2, 0x1d},
- {R367TER_RESERVED_1, 0x00},
- {R367TER_RESERVED_2, 0x00},
- {R367TER_RESERVED_3, 0x00},
- {R367TER_CAS_CTL, 0x44},
- {R367TER_CAS_FREQ, 0xb3},
- {R367TER_CAS_DAGCGAIN, 0x12},
- {R367TER_SYR_CTL, 0x04},
- {R367TER_SYR_STAT, 0x10},
- {R367TER_SYR_NCO1, 0x00},
- {R367TER_SYR_NCO2, 0x00},
- {R367TER_SYR_OFFSET1, 0x00},
- {R367TER_SYR_OFFSET2, 0x00},
- {R367TER_FFT_CTL, 0x00},
- {R367TER_SCR_CTL, 0x70},
- {R367TER_PPM_CTL1, 0xf8},
- {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */
- {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */
- {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */
- {R367TER_TRL_TIME1, 0x1d},
- {R367TER_TRL_TIME2, 0xfc},
- {R367TER_CRL_CTL, 0x24},
- {R367TER_CRL_FREQ1, 0xad},
- {R367TER_CRL_FREQ2, 0x9d},
- {R367TER_CRL_FREQ3, 0xff},
- {R367TER_CHC_CTL, 0x01},
- {R367TER_CHC_SNR, 0xf0},
- {R367TER_BDI_CTL, 0x00},
- {R367TER_DMP_CTL, 0x00},
- {R367TER_TPS_RCVD1, 0x30},
- {R367TER_TPS_RCVD2, 0x02},
- {R367TER_TPS_RCVD3, 0x01},
- {R367TER_TPS_RCVD4, 0x00},
- {R367TER_TPS_ID_CELL1, 0x00},
- {R367TER_TPS_ID_CELL2, 0x00},
- {R367TER_TPS_RCVD5_SET1, 0x02},
- {R367TER_TPS_SET2, 0x02},
- {R367TER_TPS_SET3, 0x01},
- {R367TER_TPS_CTL, 0x00},
- {R367TER_CTL_FFTOSNUM, 0x34},
- {R367TER_TESTSELECT, 0x09},
- {R367TER_MSC_REV, 0x0a},
- {R367TER_PIR_CTL, 0x00},
- {R367TER_SNR_CARRIER1, 0xa1},
- {R367TER_SNR_CARRIER2, 0x9a},
- {R367TER_PPM_CPAMP, 0x2c},
- {R367TER_TSM_AP0, 0x00},
- {R367TER_TSM_AP1, 0x00},
- {R367TER_TSM_AP2 , 0x00},
- {R367TER_TSM_AP3, 0x00},
- {R367TER_TSM_AP4, 0x00},
- {R367TER_TSM_AP5, 0x00},
- {R367TER_TSM_AP6, 0x00},
- {R367TER_TSM_AP7, 0x00},
- {R367TER_TSTRES, 0x00},
- {R367TER_ANACTRL, 0x0D},/* PLL stoped, restart at init!!! */
- {R367TER_TSTBUS, 0x00},
- {R367TER_TSTRATE, 0x00},
- {R367TER_CONSTMODE, 0x01},
- {R367TER_CONSTCARR1, 0x00},
- {R367TER_CONSTCARR2, 0x00},
- {R367TER_ICONSTEL, 0x0a},
- {R367TER_QCONSTEL, 0x15},
- {R367TER_TSTBISTRES0, 0x00},
- {R367TER_TSTBISTRES1, 0x00},
- {R367TER_TSTBISTRES2, 0x28},
- {R367TER_TSTBISTRES3, 0x00},
- {R367TER_RF_AGC1, 0xff},
- {R367TER_RF_AGC2, 0x83},
- {R367TER_ANADIGCTRL, 0x19},
- {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */
- {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */
- {R367TER_PLLSETUP, 0x18},
- {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */
- {R367TER_TSTBIST, 0x00},
- {R367TER_PAD_COMP_CTRL, 0x00},
- {R367TER_PAD_COMP_WR, 0x00},
- {R367TER_PAD_COMP_RD, 0xe0},
- {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00},
- {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00},
- {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00},
- {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00},
- {R367TER_SYR_FLAG, 0x00},
- {R367TER_CRL_TARGET1, 0x00},
- {R367TER_CRL_TARGET2, 0x00},
- {R367TER_CRL_TARGET3, 0x00},
- {R367TER_CRL_TARGET4, 0x00},
- {R367TER_CRL_FLAG, 0x00},
- {R367TER_TRL_TARGET1, 0x00},
- {R367TER_TRL_TARGET2, 0x00},
- {R367TER_TRL_CHC, 0x00},
- {R367TER_CHC_SNR_TARG, 0x00},
- {R367TER_TOP_TRACK, 0x00},
- {R367TER_TRACKER_FREE1, 0x00},
- {R367TER_ERROR_CRL1, 0x00},
- {R367TER_ERROR_CRL2, 0x00},
- {R367TER_ERROR_CRL3, 0x00},
- {R367TER_ERROR_CRL4, 0x00},
- {R367TER_DEC_NCO1, 0x2c},
- {R367TER_DEC_NCO2, 0x0f},
- {R367TER_DEC_NCO3, 0x20},
- {R367TER_SNR, 0xf1},
- {R367TER_SYR_FFTADJ1, 0x00},
- {R367TER_SYR_FFTADJ2, 0x00},
- {R367TER_SYR_CHCADJ1, 0x00},
- {R367TER_SYR_CHCADJ2, 0x00},
- {R367TER_SYR_OFF, 0x00},
- {R367TER_PPM_OFFSET1, 0x00},
- {R367TER_PPM_OFFSET2, 0x03},
- {R367TER_TRACKER_FREE2, 0x00},
- {R367TER_DEBG_LT10, 0x00},
- {R367TER_DEBG_LT11, 0x00},
- {R367TER_DEBG_LT12, 0x00},
- {R367TER_DEBG_LT13, 0x00},
- {R367TER_DEBG_LT14, 0x00},
- {R367TER_DEBG_LT15, 0x00},
- {R367TER_DEBG_LT16, 0x00},
- {R367TER_DEBG_LT17, 0x00},
- {R367TER_DEBG_LT18, 0x00},
- {R367TER_DEBG_LT19, 0x00},
- {R367TER_DEBG_LT1A, 0x00},
- {R367TER_DEBG_LT1B, 0x00},
- {R367TER_DEBG_LT1C, 0x00},
- {R367TER_DEBG_LT1D, 0x00},
- {R367TER_DEBG_LT1E, 0x00},
- {R367TER_DEBG_LT1F, 0x00},
- {R367TER_RCCFGH, 0x00},
- {R367TER_RCCFGM, 0x00},
- {R367TER_RCCFGL, 0x00},
- {R367TER_RCINSDELH, 0x00},
- {R367TER_RCINSDELM, 0x00},
- {R367TER_RCINSDELL, 0x00},
- {R367TER_RCSTATUS, 0x00},
- {R367TER_RCSPEED, 0x6f},
- {R367TER_RCDEBUGM, 0xe7},
- {R367TER_RCDEBUGL, 0x9b},
- {R367TER_RCOBSCFG, 0x00},
- {R367TER_RCOBSM, 0x00},
- {R367TER_RCOBSL, 0x00},
- {R367TER_RCFECSPY, 0x00},
- {R367TER_RCFSPYCFG, 0x00},
- {R367TER_RCFSPYDATA, 0x00},
- {R367TER_RCFSPYOUT, 0x00},
- {R367TER_RCFSTATUS, 0x00},
- {R367TER_RCFGOODPACK, 0x00},
- {R367TER_RCFPACKCNT, 0x00},
- {R367TER_RCFSPYMISC, 0x00},
- {R367TER_RCFBERCPT4, 0x00},
- {R367TER_RCFBERCPT3, 0x00},
- {R367TER_RCFBERCPT2, 0x00},
- {R367TER_RCFBERCPT1, 0x00},
- {R367TER_RCFBERCPT0, 0x00},
- {R367TER_RCFBERERR2, 0x00},
- {R367TER_RCFBERERR1, 0x00},
- {R367TER_RCFBERERR0, 0x00},
- {R367TER_RCFSTATESM, 0x00},
- {R367TER_RCFSTATESL, 0x00},
- {R367TER_RCFSPYBER, 0x00},
- {R367TER_RCFSPYDISTM, 0x00},
- {R367TER_RCFSPYDISTL, 0x00},
- {R367TER_RCFSPYOBS7, 0x00},
- {R367TER_RCFSPYOBS6, 0x00},
- {R367TER_RCFSPYOBS5, 0x00},
- {R367TER_RCFSPYOBS4, 0x00},
- {R367TER_RCFSPYOBS3, 0x00},
- {R367TER_RCFSPYOBS2, 0x00},
- {R367TER_RCFSPYOBS1, 0x00},
- {R367TER_RCFSPYOBS0, 0x00},
- {R367TER_TSGENERAL, 0x00},
- {R367TER_RC1SPEED, 0x6f},
- {R367TER_TSGSTATUS, 0x18},
- {R367TER_FECM, 0x01},
- {R367TER_VTH12, 0xff},
- {R367TER_VTH23, 0xa1},
- {R367TER_VTH34, 0x64},
- {R367TER_VTH56, 0x40},
- {R367TER_VTH67, 0x00},
- {R367TER_VTH78, 0x2c},
- {R367TER_VITCURPUN, 0x12},
- {R367TER_VERROR, 0x01},
- {R367TER_PRVIT, 0x3f},
- {R367TER_VAVSRVIT, 0x00},
- {R367TER_VSTATUSVIT, 0xbd},
- {R367TER_VTHINUSE, 0xa1},
- {R367TER_KDIV12, 0x20},
- {R367TER_KDIV23, 0x40},
- {R367TER_KDIV34, 0x20},
- {R367TER_KDIV56, 0x30},
- {R367TER_KDIV67, 0x00},
- {R367TER_KDIV78, 0x30},
- {R367TER_SIGPOWER, 0x54},
- {R367TER_DEMAPVIT, 0x40},
- {R367TER_VITSCALE, 0x00},
- {R367TER_FFEC1PRG, 0x00},
- {R367TER_FVITCURPUN, 0x12},
- {R367TER_FVERROR, 0x01},
- {R367TER_FVSTATUSVIT, 0xbd},
- {R367TER_DEBUG_LT1, 0x00},
- {R367TER_DEBUG_LT2, 0x00},
- {R367TER_DEBUG_LT3, 0x00},
- {R367TER_TSTSFMET, 0x00},
- {R367TER_SELOUT, 0x00},
- {R367TER_TSYNC, 0x00},
- {R367TER_TSTERR, 0x00},
- {R367TER_TSFSYNC, 0x00},
- {R367TER_TSTSFERR, 0x00},
- {R367TER_TSTTSSF1, 0x01},
- {R367TER_TSTTSSF2, 0x1f},
- {R367TER_TSTTSSF3, 0x00},
- {R367TER_TSTTS1, 0x00},
- {R367TER_TSTTS2, 0x1f},
- {R367TER_TSTTS3, 0x01},
- {R367TER_TSTTS4, 0x00},
- {R367TER_TSTTSRC, 0x00},
- {R367TER_TSTTSRS, 0x00},
- {R367TER_TSSTATEM, 0xb0},
- {R367TER_TSSTATEL, 0x40},
- {R367TER_TSCFGH, 0xC0},
- {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */
- {R367TER_TSCFGL, 0x20},
- {R367TER_TSSYNC, 0x00},
- {R367TER_TSINSDELH, 0x00},
- {R367TER_TSINSDELM, 0x00},
- {R367TER_TSINSDELL, 0x00},
- {R367TER_TSDIVN, 0x03},
- {R367TER_TSDIVPM, 0x00},
- {R367TER_TSDIVPL, 0x00},
- {R367TER_TSDIVQM, 0x00},
- {R367TER_TSDIVQL, 0x00},
- {R367TER_TSDILSTKM, 0x00},
- {R367TER_TSDILSTKL, 0x00},
- {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */
- {R367TER_TSSTATUS, 0x81},
- {R367TER_TSSTATUS2, 0x6a},
- {R367TER_TSBITRATEM, 0x0f},
- {R367TER_TSBITRATEL, 0xc6},
- {R367TER_TSPACKLENM, 0x00},
- {R367TER_TSPACKLENL, 0xfc},
- {R367TER_TSBLOCLENM, 0x0a},
- {R367TER_TSBLOCLENL, 0x80},
- {R367TER_TSDLYH, 0x90},
- {R367TER_TSDLYM, 0x68},
- {R367TER_TSDLYL, 0x01},
- {R367TER_TSNPDAV, 0x00},
- {R367TER_TSBUFSTATH, 0x00},
- {R367TER_TSBUFSTATM, 0x00},
- {R367TER_TSBUFSTATL, 0x00},
- {R367TER_TSDEBUGM, 0xcf},
- {R367TER_TSDEBUGL, 0x1e},
- {R367TER_TSDLYSETH, 0x00},
- {R367TER_TSDLYSETM, 0x68},
- {R367TER_TSDLYSETL, 0x00},
- {R367TER_TSOBSCFG, 0x00},
- {R367TER_TSOBSM, 0x47},
- {R367TER_TSOBSL, 0x1f},
- {R367TER_ERRCTRL1, 0x95},
- {R367TER_ERRCNT1H, 0x80},
- {R367TER_ERRCNT1M, 0x00},
- {R367TER_ERRCNT1L, 0x00},
- {R367TER_ERRCTRL2, 0x95},
- {R367TER_ERRCNT2H, 0x00},
- {R367TER_ERRCNT2M, 0x00},
- {R367TER_ERRCNT2L, 0x00},
- {R367TER_FECSPY, 0x88},
- {R367TER_FSPYCFG, 0x2c},
- {R367TER_FSPYDATA, 0x3a},
- {R367TER_FSPYOUT, 0x06},
- {R367TER_FSTATUS, 0x61},
- {R367TER_FGOODPACK, 0xff},
- {R367TER_FPACKCNT, 0xff},
- {R367TER_FSPYMISC, 0x66},
- {R367TER_FBERCPT4, 0x00},
- {R367TER_FBERCPT3, 0x00},
- {R367TER_FBERCPT2, 0x36},
- {R367TER_FBERCPT1, 0x36},
- {R367TER_FBERCPT0, 0x14},
- {R367TER_FBERERR2, 0x00},
- {R367TER_FBERERR1, 0x03},
- {R367TER_FBERERR0, 0x28},
- {R367TER_FSTATESM, 0x00},
- {R367TER_FSTATESL, 0x02},
- {R367TER_FSPYBER, 0x00},
- {R367TER_FSPYDISTM, 0x01},
- {R367TER_FSPYDISTL, 0x9f},
- {R367TER_FSPYOBS7, 0xc9},
- {R367TER_FSPYOBS6, 0x99},
- {R367TER_FSPYOBS5, 0x08},
- {R367TER_FSPYOBS4, 0xec},
- {R367TER_FSPYOBS3, 0x01},
- {R367TER_FSPYOBS2, 0x0f},
- {R367TER_FSPYOBS1, 0xf5},
- {R367TER_FSPYOBS0, 0x08},
- {R367TER_SFDEMAP, 0x40},
- {R367TER_SFERROR, 0x00},
- {R367TER_SFAVSR, 0x30},
- {R367TER_SFECSTATUS, 0xcc},
- {R367TER_SFKDIV12, 0x20},
- {R367TER_SFKDIV23, 0x40},
- {R367TER_SFKDIV34, 0x20},
- {R367TER_SFKDIV56, 0x20},
- {R367TER_SFKDIV67, 0x00},
- {R367TER_SFKDIV78, 0x20},
- {R367TER_SFDILSTKM, 0x00},
- {R367TER_SFDILSTKL, 0x00},
- {R367TER_SFSTATUS, 0xb5},
- {R367TER_SFDLYH, 0x90},
- {R367TER_SFDLYM, 0x60},
- {R367TER_SFDLYL, 0x01},
- {R367TER_SFDLYSETH, 0xc0},
- {R367TER_SFDLYSETM, 0x60},
- {R367TER_SFDLYSETL, 0x00},
- {R367TER_SFOBSCFG, 0x00},
- {R367TER_SFOBSM, 0x47},
- {R367TER_SFOBSL, 0x05},
- {R367TER_SFECINFO, 0x40},
- {R367TER_SFERRCTRL, 0x74},
- {R367TER_SFERRCNTH, 0x80},
- {R367TER_SFERRCNTM , 0x00},
- {R367TER_SFERRCNTL, 0x00},
- {R367TER_SYMBRATEM, 0x2f},
- {R367TER_SYMBRATEL, 0x50},
- {R367TER_SYMBSTATUS, 0x7f},
- {R367TER_SYMBCFG, 0x00},
- {R367TER_SYMBFIFOM, 0xf4},
- {R367TER_SYMBFIFOL, 0x0d},
- {R367TER_SYMBOFFSM, 0xf0},
- {R367TER_SYMBOFFSL, 0x2d},
- {R367TER_DEBUG_LT4, 0x00},
- {R367TER_DEBUG_LT5, 0x00},
- {R367TER_DEBUG_LT6, 0x00},
- {R367TER_DEBUG_LT7, 0x00},
- {R367TER_DEBUG_LT8, 0x00},
- {R367TER_DEBUG_LT9, 0x00},
+ /* flags for operation control */
+ u8 use_i2c_gatectrl;
+ u8 deftabs;
+ u8 reinit_on_setfrontend;
+ u8 auto_if_khz;
+ enum active_demod_state activedemod;
};
#define RF_LOOKUP_TABLE_SIZE 31
@@ -571,197 +128,6 @@ static const s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_S
}
};
-static struct st_register def0367cab[STV0367CAB_NBREGS] = {
- {R367CAB_ID, 0x60},
- {R367CAB_I2CRPT, 0xa0},
- /*{R367CAB_I2CRPT, 0x22},*/
- {R367CAB_TOPCTRL, 0x10},
- {R367CAB_IOCFG0, 0x80},
- {R367CAB_DAC0R, 0x00},
- {R367CAB_IOCFG1, 0x00},
- {R367CAB_DAC1R, 0x00},
- {R367CAB_IOCFG2, 0x00},
- {R367CAB_SDFR, 0x00},
- {R367CAB_AUX_CLK, 0x00},
- {R367CAB_FREESYS1, 0x00},
- {R367CAB_FREESYS2, 0x00},
- {R367CAB_FREESYS3, 0x00},
- {R367CAB_GPIO_CFG, 0x55},
- {R367CAB_GPIO_CMD, 0x01},
- {R367CAB_TSTRES, 0x00},
- {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/
- {R367CAB_TSTBUS, 0x00},
- {R367CAB_RF_AGC1, 0xea},
- {R367CAB_RF_AGC2, 0x82},
- {R367CAB_ANADIGCTRL, 0x0b},
- {R367CAB_PLLMDIV, 0x01},
- {R367CAB_PLLNDIV, 0x08},
- {R367CAB_PLLSETUP, 0x18},
- {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */
- {R367CAB_TSTBIST, 0x00},
- {R367CAB_CTRL_1, 0x00},
- {R367CAB_CTRL_2, 0x03},
- {R367CAB_IT_STATUS1, 0x2b},
- {R367CAB_IT_STATUS2, 0x08},
- {R367CAB_IT_EN1, 0x00},
- {R367CAB_IT_EN2, 0x00},
- {R367CAB_CTRL_STATUS, 0x04},
- {R367CAB_TEST_CTL, 0x00},
- {R367CAB_AGC_CTL, 0x73},
- {R367CAB_AGC_IF_CFG, 0x50},
- {R367CAB_AGC_RF_CFG, 0x00},
- {R367CAB_AGC_PWM_CFG, 0x03},
- {R367CAB_AGC_PWR_REF_L, 0x5a},
- {R367CAB_AGC_PWR_REF_H, 0x00},
- {R367CAB_AGC_RF_TH_L, 0xff},
- {R367CAB_AGC_RF_TH_H, 0x07},
- {R367CAB_AGC_IF_LTH_L, 0x00},
- {R367CAB_AGC_IF_LTH_H, 0x08},
- {R367CAB_AGC_IF_HTH_L, 0xff},
- {R367CAB_AGC_IF_HTH_H, 0x07},
- {R367CAB_AGC_PWR_RD_L, 0xa0},
- {R367CAB_AGC_PWR_RD_M, 0xe9},
- {R367CAB_AGC_PWR_RD_H, 0x03},
- {R367CAB_AGC_PWM_IFCMD_L, 0xe4},
- {R367CAB_AGC_PWM_IFCMD_H, 0x00},
- {R367CAB_AGC_PWM_RFCMD_L, 0xff},
- {R367CAB_AGC_PWM_RFCMD_H, 0x07},
- {R367CAB_IQDEM_CFG, 0x01},
- {R367CAB_MIX_NCO_LL, 0x22},
- {R367CAB_MIX_NCO_HL, 0x96},
- {R367CAB_MIX_NCO_HH, 0x55},
- {R367CAB_SRC_NCO_LL, 0xff},
- {R367CAB_SRC_NCO_LH, 0x0c},
- {R367CAB_SRC_NCO_HL, 0xf5},
- {R367CAB_SRC_NCO_HH, 0x20},
- {R367CAB_IQDEM_GAIN_SRC_L, 0x06},
- {R367CAB_IQDEM_GAIN_SRC_H, 0x01},
- {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe},
- {R367CAB_IQDEM_DCRM_CFG_LH, 0xff},
- {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f},
- {R367CAB_IQDEM_DCRM_CFG_HH, 0x00},
- {R367CAB_IQDEM_ADJ_COEFF0, 0x34},
- {R367CAB_IQDEM_ADJ_COEFF1, 0xae},
- {R367CAB_IQDEM_ADJ_COEFF2, 0x46},
- {R367CAB_IQDEM_ADJ_COEFF3, 0x77},
- {R367CAB_IQDEM_ADJ_COEFF4, 0x96},
- {R367CAB_IQDEM_ADJ_COEFF5, 0x69},
- {R367CAB_IQDEM_ADJ_COEFF6, 0xc7},
- {R367CAB_IQDEM_ADJ_COEFF7, 0x01},
- {R367CAB_IQDEM_ADJ_EN, 0x04},
- {R367CAB_IQDEM_ADJ_AGC_REF, 0x94},
- {R367CAB_ALLPASSFILT1, 0xc9},
- {R367CAB_ALLPASSFILT2, 0x2d},
- {R367CAB_ALLPASSFILT3, 0xa3},
- {R367CAB_ALLPASSFILT4, 0xfb},
- {R367CAB_ALLPASSFILT5, 0xf6},
- {R367CAB_ALLPASSFILT6, 0x45},
- {R367CAB_ALLPASSFILT7, 0x6f},
- {R367CAB_ALLPASSFILT8, 0x7e},
- {R367CAB_ALLPASSFILT9, 0x05},
- {R367CAB_ALLPASSFILT10, 0x0a},
- {R367CAB_ALLPASSFILT11, 0x51},
- {R367CAB_TRL_AGC_CFG, 0x20},
- {R367CAB_TRL_LPF_CFG, 0x28},
- {R367CAB_TRL_LPF_ACQ_GAIN, 0x44},
- {R367CAB_TRL_LPF_TRK_GAIN, 0x22},
- {R367CAB_TRL_LPF_OUT_GAIN, 0x03},
- {R367CAB_TRL_LOCKDET_LTH, 0x04},
- {R367CAB_TRL_LOCKDET_HTH, 0x11},
- {R367CAB_TRL_LOCKDET_TRGVAL, 0x20},
- {R367CAB_IQ_QAM, 0x01},
- {R367CAB_FSM_STATE, 0xa0},
- {R367CAB_FSM_CTL, 0x08},
- {R367CAB_FSM_STS, 0x0c},
- {R367CAB_FSM_SNR0_HTH, 0x00},
- {R367CAB_FSM_SNR1_HTH, 0x00},
- {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */
- {R367CAB_FSM_SNR0_LTH, 0x00},
- {R367CAB_FSM_SNR1_LTH, 0x00},
- {R367CAB_FSM_EQA1_HTH, 0x00},
- {R367CAB_FSM_TEMPO, 0x32},
- {R367CAB_FSM_CONFIG, 0x03},
- {R367CAB_EQU_I_TESTTAP_L, 0x11},
- {R367CAB_EQU_I_TESTTAP_M, 0x00},
- {R367CAB_EQU_I_TESTTAP_H, 0x00},
- {R367CAB_EQU_TESTAP_CFG, 0x00},
- {R367CAB_EQU_Q_TESTTAP_L, 0xff},
- {R367CAB_EQU_Q_TESTTAP_M, 0x00},
- {R367CAB_EQU_Q_TESTTAP_H, 0x00},
- {R367CAB_EQU_TAP_CTRL, 0x00},
- {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11},
- {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05},
- {R367CAB_EQU_CTR_HIPOW_L, 0x00},
- {R367CAB_EQU_CTR_HIPOW_H, 0x00},
- {R367CAB_EQU_I_EQU_LO, 0xef},
- {R367CAB_EQU_I_EQU_HI, 0x00},
- {R367CAB_EQU_Q_EQU_LO, 0xee},
- {R367CAB_EQU_Q_EQU_HI, 0x00},
- {R367CAB_EQU_MAPPER, 0xc5},
- {R367CAB_EQU_SWEEP_RATE, 0x80},
- {R367CAB_EQU_SNR_LO, 0x64},
- {R367CAB_EQU_SNR_HI, 0x03},
- {R367CAB_EQU_GAMMA_LO, 0x00},
- {R367CAB_EQU_GAMMA_HI, 0x00},
- {R367CAB_EQU_ERR_GAIN, 0x36},
- {R367CAB_EQU_RADIUS, 0xaa},
- {R367CAB_EQU_FFE_MAINTAP, 0x00},
- {R367CAB_EQU_FFE_LEAKAGE, 0x63},
- {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf},
- {R367CAB_EQU_GAIN_WIDE, 0x88},
- {R367CAB_EQU_GAIN_NARROW, 0x41},
- {R367CAB_EQU_CTR_LPF_GAIN, 0xd1},
- {R367CAB_EQU_CRL_LPF_GAIN, 0xa7},
- {R367CAB_EQU_GLOBAL_GAIN, 0x06},
- {R367CAB_EQU_CRL_LD_SEN, 0x85},
- {R367CAB_EQU_CRL_LD_VAL, 0xe2},
- {R367CAB_EQU_CRL_TFR, 0x20},
- {R367CAB_EQU_CRL_BISTH_LO, 0x00},
- {R367CAB_EQU_CRL_BISTH_HI, 0x00},
- {R367CAB_EQU_SWEEP_RANGE_LO, 0x00},
- {R367CAB_EQU_SWEEP_RANGE_HI, 0x00},
- {R367CAB_EQU_CRL_LIMITER, 0x40},
- {R367CAB_EQU_MODULUS_MAP, 0x90},
- {R367CAB_EQU_PNT_GAIN, 0xa7},
- {R367CAB_FEC_AC_CTR_0, 0x16},
- {R367CAB_FEC_AC_CTR_1, 0x0b},
- {R367CAB_FEC_AC_CTR_2, 0x88},
- {R367CAB_FEC_AC_CTR_3, 0x02},
- {R367CAB_FEC_STATUS, 0x12},
- {R367CAB_RS_COUNTER_0, 0x7d},
- {R367CAB_RS_COUNTER_1, 0xd0},
- {R367CAB_RS_COUNTER_2, 0x19},
- {R367CAB_RS_COUNTER_3, 0x0b},
- {R367CAB_RS_COUNTER_4, 0xa3},
- {R367CAB_RS_COUNTER_5, 0x00},
- {R367CAB_BERT_0, 0x01},
- {R367CAB_BERT_1, 0x25},
- {R367CAB_BERT_2, 0x41},
- {R367CAB_BERT_3, 0x39},
- {R367CAB_OUTFORMAT_0, 0xc2},
- {R367CAB_OUTFORMAT_1, 0x22},
- {R367CAB_SMOOTHER_2, 0x28},
- {R367CAB_TSMF_CTRL_0, 0x01},
- {R367CAB_TSMF_CTRL_1, 0xc6},
- {R367CAB_TSMF_CTRL_3, 0x43},
- {R367CAB_TS_ON_ID_0, 0x00},
- {R367CAB_TS_ON_ID_1, 0x00},
- {R367CAB_TS_ON_ID_2, 0x00},
- {R367CAB_TS_ON_ID_3, 0x00},
- {R367CAB_RE_STATUS_0, 0x00},
- {R367CAB_RE_STATUS_1, 0x00},
- {R367CAB_RE_STATUS_2, 0x00},
- {R367CAB_RE_STATUS_3, 0x00},
- {R367CAB_TS_STATUS_0, 0x00},
- {R367CAB_TS_STATUS_1, 0x00},
- {R367CAB_TS_STATUS_2, 0xa0},
- {R367CAB_TS_STATUS_3, 0x00},
- {R367CAB_T_O_ID_0, 0x00},
- {R367CAB_T_O_ID_1, 0x00},
- {R367CAB_T_O_ID_2, 0x00},
- {R367CAB_T_O_ID_3, 0x00},
-};
-
static
int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len)
{
@@ -899,6 +265,78 @@ static u8 stv0367_getbits(u8 reg, u32 label)
return (reg & mask) >> pos;
}
#endif
+
+static void stv0367_write_table(struct stv0367_state *state,
+ const struct st_register *deftab)
+{
+ int i = 0;
+
+ while (1) {
+ if (!deftab[i].addr)
+ break;
+ stv0367_writereg(state, deftab[i].addr, deftab[i].value);
+ i++;
+ }
+}
+
+static void stv0367_pll_setup(struct stv0367_state *state,
+ u32 icspeed, u32 xtal)
+{
+ /* note on regs: R367TER_* and R367CAB_* defines each point to
+ * 0xf0d8, so just use R367TER_ for both cases
+ */
+
+ switch (icspeed) {
+ case STV0367_ICSPEED_58000:
+ switch (xtal) {
+ default:
+ case 27000000:
+ dprintk("STV0367 SetCLKgen for 58MHz IC and 27Mhz crystal\n");
+ /* PLLMDIV: 27, PLLNDIV: 232 */
+ stv0367_writereg(state, R367TER_PLLMDIV, 0x1b);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0xe8);
+ break;
+ }
+ break;
+ default:
+ case STV0367_ICSPEED_53125:
+ switch (xtal) {
+ /* set internal freq to 53.125MHz */
+ case 16000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0x2);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x1b);
+ break;
+ case 25000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0xa);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
+ break;
+ default:
+ case 27000000:
+ dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n");
+ stv0367_writereg(state, R367TER_PLLMDIV, 0x1);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x8);
+ break;
+ case 30000000:
+ stv0367_writereg(state, R367TER_PLLMDIV, 0xc);
+ stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
+ break;
+ }
+ }
+
+ stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
+}
+
+static int stv0367_get_if_khz(struct stv0367_state *state, u32 *ifkhz)
+{
+ if (state->auto_if_khz && state->fe.ops.tuner_ops.get_if_frequency) {
+ state->fe.ops.tuner_ops.get_if_frequency(&state->fe, ifkhz);
+ *ifkhz = *ifkhz / 1000; /* hz -> khz */
+ } else
+ *ifkhz = state->config->if_khz;
+
+ return 0;
+}
+
static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct stv0367_state *state = fe->demodulator_priv;
@@ -1260,9 +698,9 @@ stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state,
dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd);
if (CPAMPvalue < CPAMPMin) {
CPAMPStatus = FE_TER_NOCPAMP;
- printk(KERN_ERR "CPAMP failed\n");
+ dprintk("%s: CPAMP failed\n", __func__);
} else {
- printk(KERN_ERR "CPAMP OK !\n");
+ dprintk("%s: CPAMP OK !\n", __func__);
CPAMPStatus = FE_TER_CPAMPOK;
}
@@ -1538,41 +976,15 @@ static int stv0367ter_init(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367ter_state *ter_state = state->ter_state;
- int i;
dprintk("%s:\n", __func__);
ter_state->pBER = 0;
- for (i = 0; i < STV0367TER_NBREGS; i++)
- stv0367_writereg(state, def0367ter[i].addr,
- def0367ter[i].value);
+ stv0367_write_table(state,
+ stv0367_deftabs[state->deftabs][STV0367_TAB_TER]);
- switch (state->config->xtal) {
- /*set internal freq to 53.125MHz */
- case 16000000:
- stv0367_writereg(state, R367TER_PLLMDIV, 0x2);
- stv0367_writereg(state, R367TER_PLLNDIV, 0x1b);
- stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
- break;
- case 25000000:
- stv0367_writereg(state, R367TER_PLLMDIV, 0xa);
- stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
- stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
- break;
- default:
- case 27000000:
- dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n");
- stv0367_writereg(state, R367TER_PLLMDIV, 0x1);
- stv0367_writereg(state, R367TER_PLLNDIV, 0x8);
- stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
- break;
- case 30000000:
- stv0367_writereg(state, R367TER_PLLMDIV, 0xc);
- stv0367_writereg(state, R367TER_PLLNDIV, 0x55);
- stv0367_writereg(state, R367TER_PLLSETUP, 0x18);
- break;
- }
+ stv0367_pll_setup(state, STV0367_ICSPEED_53125, state->config->xtal);
stv0367_writereg(state, R367TER_I2CRPT, 0xa0);
stv0367_writereg(state, R367TER_ANACTRL, 0x00);
@@ -1598,10 +1010,12 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
u8 /*constell,*/ counter;
s8 step;
s32 timing_offset = 0;
- u32 trl_nomrate = 0, InternalFreq = 0, temp = 0;
+ u32 trl_nomrate = 0, InternalFreq = 0, temp = 0, ifkhz = 0;
dprintk("%s:\n", __func__);
+ stv0367_get_if_khz(state, &ifkhz);
+
ter_state->frequency = p->frequency;
ter_state->force = FE_TER_FORCENONE
+ stv0367_readbits(state, F367TER_FORCE) * 2;
@@ -1704,8 +1118,7 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
stv0367_readbits(state, F367TER_GAIN_SRC_LO);
temp = (int)
- ((InternalFreq - state->config->if_khz) * (1 << 16)
- / (InternalFreq));
+ ((InternalFreq - ifkhz) * (1 << 16) / (InternalFreq));
dprintk("DEROT temp=0x%x\n", temp);
stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256);
@@ -1824,13 +1237,14 @@ static int stv0367ter_set_frontend(struct dvb_frontend *fe)
s8 num_trials, index;
u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF };
- stv0367ter_init(fe);
+ if (state->reinit_on_setfrontend)
+ stv0367ter_init(fe);
if (fe->ops.tuner_ops.set_params) {
- if (fe->ops.i2c_gate_ctrl)
+ if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
+ if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
@@ -2025,7 +1439,7 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe,
return 0;
}
-static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr)
+static u32 stv0367ter_snr_readreg(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
u32 snru32 = 0;
@@ -2041,10 +1455,16 @@ static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr)
cpt++;
}
-
snru32 /= 10;/*average on 10 values*/
- *snr = snru32 / 1000;
+ return snru32;
+}
+
+static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ u32 snrval = stv0367ter_snr_readreg(fe);
+
+ *snr = snrval / 1000;
return 0;
}
@@ -2089,7 +1509,8 @@ static int stv0367ter_read_status(struct dvb_frontend *fe,
*status = 0;
if (stv0367_readbits(state, F367TER_LK)) {
- *status |= FE_HAS_LOCK;
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI
+ | FE_HAS_SYNC | FE_HAS_LOCK;
dprintk("%s: stv0367 has locked\n", __func__);
}
@@ -2321,6 +1742,12 @@ struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
state->fe.demodulator_priv = state;
state->chip_id = stv0367_readreg(state, 0xf000);
+ /* demod operation options */
+ state->use_i2c_gatectrl = 1;
+ state->deftabs = STV0367_DEFTAB_GENERIC;
+ state->reinit_on_setfrontend = 1;
+ state->auto_if_khz = 0;
+
dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id);
/* check if the demod is there */
@@ -2423,11 +1850,11 @@ static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
case FE_CAB_MOD_QAM64:
stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82);
stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a);
- if (SymbolRate > 45000000) {
+ if (SymbolRate > 4500000) {
stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0);
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5);
- } else if (SymbolRate > 25000000) {
+ } else if (SymbolRate > 2500000) {
stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0);
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6);
@@ -2445,9 +1872,9 @@ static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76);
stv0367_writereg(state, R367CAB_FSM_STATE, 0x90);
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1);
- if (SymbolRate > 45000000)
+ if (SymbolRate > 4500000)
stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7);
- else if (SymbolRate > 25000000)
+ else if (SymbolRate > 2500000)
stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6);
else
stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97);
@@ -2460,9 +1887,9 @@ static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94);
stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a);
stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0);
- if (SymbolRate > 45000000)
+ if (SymbolRate > 4500000)
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
- else if (SymbolRate > 25000000)
+ else if (SymbolRate > 2500000)
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1);
else
stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1);
@@ -2731,7 +2158,20 @@ static int stv0367cab_read_status(struct dvb_frontend *fe,
*status = 0;
- if (stv0367_readbits(state, F367CAB_QAMFEC_LOCK)) {
+ if (state->cab_state->state > FE_CAB_NOSIGNAL)
+ *status |= FE_HAS_SIGNAL;
+
+ if (state->cab_state->state > FE_CAB_NOCARRIER)
+ *status |= FE_HAS_CARRIER;
+
+ if (state->cab_state->state >= FE_CAB_DEMODOK)
+ *status |= FE_HAS_VITERBI;
+
+ if (state->cab_state->state >= FE_CAB_DATAOK)
+ *status |= FE_HAS_SYNC;
+
+ if (stv0367_readbits(state, (state->cab_state->qamfec_status_reg ?
+ state->cab_state->qamfec_status_reg : F367CAB_QAMFEC_LOCK))) {
*status |= FE_HAS_LOCK;
dprintk("%s: stv0367 has locked\n", __func__);
}
@@ -2777,13 +2217,11 @@ static int stv0367cab_init(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367cab_state *cab_state = state->cab_state;
- int i;
dprintk("%s:\n", __func__);
- for (i = 0; i < STV0367CAB_NBREGS; i++)
- stv0367_writereg(state, def0367cab[i].addr,
- def0367cab[i].value);
+ stv0367_write_table(state,
+ stv0367_deftabs[state->deftabs][STV0367_TAB_CAB]);
switch (state->config->ts_mode) {
case STV0367_DVBCI_CLOCK:
@@ -2831,7 +2269,7 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
{
struct stv0367cab_state *cab_state = state->cab_state;
enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC;
- u32 QAMFEC_Lock, QAM_Lock, u32_tmp,
+ u32 QAMFEC_Lock, QAM_Lock, u32_tmp, ifkhz,
LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols,
CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut;
u8 TrackAGCAccum;
@@ -2839,6 +2277,8 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
dprintk("%s:\n", __func__);
+ stv0367_get_if_khz(state, &ifkhz);
+
/* Timeouts calculation */
/* A max lock time of 25 ms is allowed for delayed AGC */
AGCTimeOut = 25;
@@ -2917,7 +2357,7 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
/* The sweep function is never used, Sweep rate must be set to 0 */
/* Set the derotator frequency in Hz */
stv0367cab_set_derot_freq(state, cab_state->adc_clk,
- (1000 * (s32)state->config->if_khz + cab_state->derot_offset));
+ (1000 * (s32)ifkhz + cab_state->derot_offset));
/* Disable the Allpass Filter when the symbol rate is out of range */
if ((p->symbol_rate > 10800000) | (p->symbol_rate < 1800000)) {
stv0367_writebits(state, F367CAB_ADJ_EN, 0);
@@ -2996,7 +2436,9 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
usleep_range(5000, 7000);
LockTime += 5;
QAMFEC_Lock = stv0367_readbits(state,
- F367CAB_QAMFEC_LOCK);
+ (state->cab_state->qamfec_status_reg ?
+ state->cab_state->qamfec_status_reg :
+ F367CAB_QAMFEC_LOCK));
} while (!QAMFEC_Lock && (LockTime < FECTimeOut));
} else
QAMFEC_Lock = 0;
@@ -3007,17 +2449,17 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state,
F367CAB_QUAD_INV);
#if 0
/* not clear for me */
- if (state->config->if_khz != 0) {
- if (state->config->if_khz > cab_state->adc_clk / 1000) {
+ if (ifkhz != 0) {
+ if (ifkhz > cab_state->adc_clk / 1000) {
cab_state->freq_khz =
FE_Cab_TunerGetFrequency(pIntParams->hTuner)
- stv0367cab_get_derot_freq(state, cab_state->adc_clk)
- - cab_state->adc_clk / 1000 + state->config->if_khz;
+ - cab_state->adc_clk / 1000 + ifkhz;
} else {
cab_state->freq_khz =
FE_Cab_TunerGetFrequency(pIntParams->hTuner)
- stv0367cab_get_derot_freq(state, cab_state->adc_clk)
- + state->config->if_khz;
+ + ifkhz;
}
} else {
cab_state->freq_khz =
@@ -3116,14 +2558,15 @@ static int stv0367cab_set_frontend(struct dvb_frontend *fe)
break;
}
- stv0367cab_init(fe);
+ if (state->reinit_on_setfrontend)
+ stv0367cab_init(fe);
/* Tuner Frequency Setting */
if (fe->ops.tuner_ops.set_params) {
- if (fe->ops.i2c_gate_ctrl)
+ if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
+ if (state->use_i2c_gatectrl && fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
@@ -3147,11 +2590,13 @@ static int stv0367cab_get_frontend(struct dvb_frontend *fe,
{
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367cab_state *cab_state = state->cab_state;
+ u32 ifkhz = 0;
enum stv0367cab_mod QAMSize;
dprintk("%s:\n", __func__);
+ stv0367_get_if_khz(state, &ifkhz);
p->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk);
QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE);
@@ -3179,19 +2624,19 @@ static int stv0367cab_get_frontend(struct dvb_frontend *fe,
dprintk("%s: tuner frequency = %d\n", __func__, p->frequency);
- if (state->config->if_khz == 0) {
+ if (ifkhz == 0) {
p->frequency +=
(stv0367cab_get_derot_freq(state, cab_state->adc_clk) -
cab_state->adc_clk / 4000);
return 0;
}
- if (state->config->if_khz > cab_state->adc_clk / 1000)
- p->frequency += (state->config->if_khz
+ if (ifkhz > cab_state->adc_clk / 1000)
+ p->frequency += (ifkhz
- stv0367cab_get_derot_freq(state, cab_state->adc_clk)
- cab_state->adc_clk / 1000);
else
- p->frequency += (state->config->if_khz
+ p->frequency += (ifkhz
- stv0367cab_get_derot_freq(state, cab_state->adc_clk));
return 0;
@@ -3278,51 +2723,61 @@ static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength)
return 0;
}
-static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr)
+static int stv0367cab_snr_power(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
- u32 noisepercentage;
enum stv0367cab_mod QAMSize;
- u32 regval = 0, temp = 0;
- int power, i;
QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE);
switch (QAMSize) {
case FE_CAB_MOD_QAM4:
- power = 21904;
- break;
+ return 21904;
case FE_CAB_MOD_QAM16:
- power = 20480;
- break;
+ return 20480;
case FE_CAB_MOD_QAM32:
- power = 23040;
- break;
+ return 23040;
case FE_CAB_MOD_QAM64:
- power = 21504;
- break;
+ return 21504;
case FE_CAB_MOD_QAM128:
- power = 23616;
- break;
+ return 23616;
case FE_CAB_MOD_QAM256:
- power = 21760;
- break;
- case FE_CAB_MOD_QAM512:
- power = 1;
- break;
+ return 21760;
case FE_CAB_MOD_QAM1024:
- power = 21280;
- break;
+ return 21280;
default:
- power = 1;
break;
}
+ return 1;
+}
+
+static int stv0367cab_snr_readreg(struct dvb_frontend *fe, int avgdiv)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u32 regval = 0;
+ int i;
+
for (i = 0; i < 10; i++) {
regval += (stv0367_readbits(state, F367CAB_SNR_LO)
+ 256 * stv0367_readbits(state, F367CAB_SNR_HI));
}
- regval /= 10; /*for average over 10 times in for loop above*/
+ if (avgdiv)
+ regval /= 10;
+
+ return regval;
+}
+
+static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ u32 noisepercentage;
+ u32 regval = 0, temp = 0;
+ int power;
+
+ power = stv0367cab_snr_power(fe);
+ regval = stv0367cab_snr_readreg(fe, 1);
+
if (regval != 0) {
temp = power
* (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER)));
@@ -3432,11 +2887,18 @@ struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
state->i2c = i2c;
state->config = config;
cab_state->search_range = 280000;
+ cab_state->qamfec_status_reg = F367CAB_QAMFEC_LOCK;
state->cab_state = cab_state;
state->fe.ops = stv0367cab_ops;
state->fe.demodulator_priv = state;
state->chip_id = stv0367_readreg(state, 0xf000);
+ /* demod operation options */
+ state->use_i2c_gatectrl = 1;
+ state->deftabs = STV0367_DEFTAB_GENERIC;
+ state->reinit_on_setfrontend = 1;
+ state->auto_if_khz = 0;
+
dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id);
/* check if the demod is there */
@@ -3452,6 +2914,428 @@ error:
}
EXPORT_SYMBOL(stv0367cab_attach);
+/*
+ * Functions for operation on Digital Devices hardware
+ */
+
+static void stv0367ddb_setup_ter(struct stv0367_state *state)
+{
+ stv0367_writereg(state, R367TER_DEBUG_LT4, 0x00);
+ stv0367_writereg(state, R367TER_DEBUG_LT5, 0x00);
+ stv0367_writereg(state, R367TER_DEBUG_LT6, 0x00); /* R367CAB_CTRL_1 */
+ stv0367_writereg(state, R367TER_DEBUG_LT7, 0x00); /* R367CAB_CTRL_2 */
+ stv0367_writereg(state, R367TER_DEBUG_LT8, 0x00);
+ stv0367_writereg(state, R367TER_DEBUG_LT9, 0x00);
+
+ /* Tuner Setup */
+ /* Buffer Q disabled, I Enabled, unsigned ADC */
+ stv0367_writereg(state, R367TER_ANADIGCTRL, 0x89);
+ stv0367_writereg(state, R367TER_DUAL_AD12, 0x04); /* ADCQ disabled */
+
+ /* Clock setup */
+ /* PLL bypassed and disabled */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x0D);
+ stv0367_writereg(state, R367TER_TOPCTRL, 0x00); /* Set OFDM */
+
+ /* IC runs at 54 MHz with a 27 MHz crystal */
+ stv0367_pll_setup(state, STV0367_ICSPEED_53125, state->config->xtal);
+
+ msleep(50);
+ /* PLL enabled and used */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x00);
+
+ state->activedemod = demod_ter;
+}
+
+static void stv0367ddb_setup_cab(struct stv0367_state *state)
+{
+ stv0367_writereg(state, R367TER_DEBUG_LT4, 0x00);
+ stv0367_writereg(state, R367TER_DEBUG_LT5, 0x01);
+ stv0367_writereg(state, R367TER_DEBUG_LT6, 0x06); /* R367CAB_CTRL_1 */
+ stv0367_writereg(state, R367TER_DEBUG_LT7, 0x03); /* R367CAB_CTRL_2 */
+ stv0367_writereg(state, R367TER_DEBUG_LT8, 0x00);
+ stv0367_writereg(state, R367TER_DEBUG_LT9, 0x00);
+
+ /* Tuner Setup */
+ /* Buffer Q disabled, I Enabled, signed ADC */
+ stv0367_writereg(state, R367TER_ANADIGCTRL, 0x8B);
+ /* ADCQ disabled */
+ stv0367_writereg(state, R367TER_DUAL_AD12, 0x04);
+
+ /* Clock setup */
+ /* PLL bypassed and disabled */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x0D);
+ /* Set QAM */
+ stv0367_writereg(state, R367TER_TOPCTRL, 0x10);
+
+ /* IC runs at 58 MHz with a 27 MHz crystal */
+ stv0367_pll_setup(state, STV0367_ICSPEED_58000, state->config->xtal);
+
+ msleep(50);
+ /* PLL enabled and used */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x00);
+
+ state->cab_state->mclk = stv0367cab_get_mclk(&state->fe,
+ state->config->xtal);
+ state->cab_state->adc_clk = stv0367cab_get_adc_freq(&state->fe,
+ state->config->xtal);
+
+ state->activedemod = demod_cab;
+}
+
+static int stv0367ddb_set_frontend(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ if (state->activedemod != demod_ter)
+ stv0367ddb_setup_ter(state);
+
+ return stv0367ter_set_frontend(fe);
+ case SYS_DVBC_ANNEX_A:
+ if (state->activedemod != demod_cab)
+ stv0367ddb_setup_cab(state);
+
+ /* protect against division error oopses */
+ if (fe->dtv_property_cache.symbol_rate == 0) {
+ printk(KERN_ERR "Invalid symbol rate\n");
+ return -EINVAL;
+ }
+
+ return stv0367cab_set_frontend(fe);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static void stv0367ddb_read_signal_strength(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ s32 signalstrength;
+
+ switch (state->activedemod) {
+ case demod_cab:
+ signalstrength = stv0367cab_get_rf_lvl(state) * 1000;
+ break;
+ default:
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ p->strength.stat[0].uvalue = signalstrength;
+}
+
+static void stv0367ddb_read_snr(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int cab_pwr;
+ u32 regval, tmpval, snrval = 0;
+
+ switch (state->activedemod) {
+ case demod_ter:
+ snrval = stv0367ter_snr_readreg(fe);
+ break;
+ case demod_cab:
+ cab_pwr = stv0367cab_snr_power(fe);
+ regval = stv0367cab_snr_readreg(fe, 0);
+
+ /* prevent division by zero */
+ if (!regval) {
+ snrval = 0;
+ break;
+ }
+
+ tmpval = (cab_pwr * 320) / regval;
+ snrval = ((tmpval != 0) ? (intlog2(tmpval) / 5581) : 0);
+ break;
+ default:
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].uvalue = snrval;
+}
+
+static void stv0367ddb_read_ucblocks(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 ucblocks = 0;
+
+ switch (state->activedemod) {
+ case demod_ter:
+ stv0367ter_read_ucblocks(fe, &ucblocks);
+ break;
+ case demod_cab:
+ stv0367cab_read_ucblcks(fe, &ucblocks);
+ break;
+ default:
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].uvalue = ucblocks;
+}
+
+static int stv0367ddb_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int ret;
+
+ switch (state->activedemod) {
+ case demod_ter:
+ ret = stv0367ter_read_status(fe, status);
+ break;
+ case demod_cab:
+ ret = stv0367cab_read_status(fe, status);
+ break;
+ default:
+ return 0;
+ }
+
+ /* stop and report on *_read_status failure */
+ if (ret)
+ return ret;
+
+ stv0367ddb_read_signal_strength(fe);
+
+ /* read carrier/noise when a carrier is detected */
+ if (*status & FE_HAS_CARRIER)
+ stv0367ddb_read_snr(fe);
+ else
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* read uncorrected blocks on FE_HAS_LOCK */
+ if (*status & FE_HAS_LOCK)
+ stv0367ddb_read_ucblocks(fe);
+ else
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+}
+
+static int stv0367ddb_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ switch (state->activedemod) {
+ case demod_ter:
+ return stv0367ter_get_frontend(fe, p);
+ case demod_cab:
+ return stv0367cab_get_frontend(fe, p);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int stv0367ddb_sleep(struct dvb_frontend *fe)
+{
+ struct stv0367_state *state = fe->demodulator_priv;
+
+ switch (state->activedemod) {
+ case demod_ter:
+ state->activedemod = demod_none;
+ return stv0367ter_sleep(fe);
+ case demod_cab:
+ state->activedemod = demod_none;
+ return stv0367cab_sleep(fe);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int stv0367ddb_init(struct stv0367_state *state)
+{
+ struct stv0367ter_state *ter_state = state->ter_state;
+ struct dtv_frontend_properties *p = &state->fe.dtv_property_cache;
+
+ stv0367_writereg(state, R367TER_TOPCTRL, 0x10);
+
+ if (stv0367_deftabs[state->deftabs][STV0367_TAB_BASE])
+ stv0367_write_table(state,
+ stv0367_deftabs[state->deftabs][STV0367_TAB_BASE]);
+
+ stv0367_write_table(state,
+ stv0367_deftabs[state->deftabs][STV0367_TAB_CAB]);
+
+ stv0367_writereg(state, R367TER_TOPCTRL, 0x00);
+ stv0367_write_table(state,
+ stv0367_deftabs[state->deftabs][STV0367_TAB_TER]);
+
+ stv0367_writereg(state, R367TER_GAIN_SRC1, 0x2A);
+ stv0367_writereg(state, R367TER_GAIN_SRC2, 0xD6);
+ stv0367_writereg(state, R367TER_INC_DEROT1, 0x55);
+ stv0367_writereg(state, R367TER_INC_DEROT2, 0x55);
+ stv0367_writereg(state, R367TER_TRL_CTL, 0x14);
+ stv0367_writereg(state, R367TER_TRL_NOMRATE1, 0xAE);
+ stv0367_writereg(state, R367TER_TRL_NOMRATE2, 0x56);
+ stv0367_writereg(state, R367TER_FEPATH_CFG, 0x0);
+
+ /* OFDM TS Setup */
+
+ stv0367_writereg(state, R367TER_TSCFGH, 0x70);
+ stv0367_writereg(state, R367TER_TSCFGM, 0xC0);
+ stv0367_writereg(state, R367TER_TSCFGL, 0x20);
+ stv0367_writereg(state, R367TER_TSSPEED, 0x40); /* Fixed at 54 MHz */
+
+ stv0367_writereg(state, R367TER_TSCFGH, 0x71);
+ stv0367_writereg(state, R367TER_TSCFGH, 0x70);
+
+ stv0367_writereg(state, R367TER_TOPCTRL, 0x10);
+
+ /* Also needed for QAM */
+ stv0367_writereg(state, R367TER_AGC12C, 0x01); /* AGC Pin setup */
+
+ stv0367_writereg(state, R367TER_AGCCTRL1, 0x8A);
+
+ /* QAM TS setup, note exact format also depends on descrambler */
+ /* settings */
+ /* Inverted Clock, Swap, serial */
+ stv0367_writereg(state, R367CAB_OUTFORMAT_0, 0x85);
+
+ /* Clock setup (PLL bypassed and disabled) */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x0D);
+
+ /* IC runs at 58 MHz with a 27 MHz crystal */
+ stv0367_pll_setup(state, STV0367_ICSPEED_58000, state->config->xtal);
+
+ /* Tuner setup */
+ /* Buffer Q disabled, I Enabled, signed ADC */
+ stv0367_writereg(state, R367TER_ANADIGCTRL, 0x8b);
+ stv0367_writereg(state, R367TER_DUAL_AD12, 0x04); /* ADCQ disabled */
+
+ /* Improves the C/N lock limit */
+ stv0367_writereg(state, R367CAB_FSM_SNR2_HTH, 0x23);
+ /* ZIF/IF Automatic mode */
+ stv0367_writereg(state, R367CAB_IQ_QAM, 0x01);
+ /* Improving burst noise performances */
+ stv0367_writereg(state, R367CAB_EQU_FFE_LEAKAGE, 0x83);
+ /* Improving ACI performances */
+ stv0367_writereg(state, R367CAB_IQDEM_ADJ_EN, 0x05);
+
+ /* PLL enabled and used */
+ stv0367_writereg(state, R367TER_ANACTRL, 0x00);
+
+ stv0367_writereg(state, R367TER_I2CRPT, (0x08 | ((5 & 0x07) << 4)));
+
+ ter_state->pBER = 0;
+ ter_state->first_lock = 0;
+ ter_state->unlock_counter = 2;
+
+ p->strength.len = 1;
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.len = 1;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.len = 1;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+}
+
+static const struct dvb_frontend_ops stv0367ddb_ops = {
+ .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBT },
+ .info = {
+ .name = "ST STV0367 DDB DVB-C/T",
+ .frequency_min = 47000000,
+ .frequency_max = 865000000,
+ .frequency_stepsize = 166667,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ .caps = /* DVB-C */
+ 0x400 |/* FE_CAN_QAM_4 */
+ FE_CAN_QAM_16 | FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+ /* DVB-T */
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_RECOVER | FE_CAN_INVERSION_AUTO |
+ FE_CAN_MUTE_TS
+ },
+ .release = stv0367_release,
+ .sleep = stv0367ddb_sleep,
+ .i2c_gate_ctrl = stv0367cab_gate_ctrl, /* valid for TER and CAB */
+ .set_frontend = stv0367ddb_set_frontend,
+ .get_frontend = stv0367ddb_get_frontend,
+ .get_tune_settings = stv0367_get_tune_settings,
+ .read_status = stv0367ddb_read_status,
+};
+
+struct dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stv0367_state *state = NULL;
+ struct stv0367ter_state *ter_state = NULL;
+ struct stv0367cab_state *cab_state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL);
+ if (ter_state == NULL)
+ goto error;
+ cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL);
+ if (cab_state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ state->config = config;
+ state->ter_state = ter_state;
+ cab_state->search_range = 280000;
+ cab_state->qamfec_status_reg = F367CAB_DESCR_SYNCSTATE;
+ state->cab_state = cab_state;
+ state->fe.ops = stv0367ddb_ops;
+ state->fe.demodulator_priv = state;
+ state->chip_id = stv0367_readreg(state, R367TER_ID);
+
+ /* demod operation options */
+ state->use_i2c_gatectrl = 0;
+ state->deftabs = STV0367_DEFTAB_DDB;
+ state->reinit_on_setfrontend = 0;
+ state->auto_if_khz = 1;
+ state->activedemod = demod_none;
+
+ dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id);
+
+ /* check if the demod is there */
+ if ((state->chip_id != 0x50) && (state->chip_id != 0x60))
+ goto error;
+
+ dev_info(&i2c->dev, "Found %s with ChipID %02X at adr %02X\n",
+ state->fe.ops.info.name, state->chip_id,
+ config->demod_address);
+
+ stv0367ddb_init(state);
+
+ return &state->fe;
+
+error:
+ kfree(cab_state);
+ kfree(ter_state);
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stv0367ddb_attach);
+
MODULE_PARM_DESC(debug, "Set debug");
MODULE_PARM_DESC(i2c_debug, "Set i2c debug");
diff --git a/drivers/media/dvb-frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h
index 26c38a0503c8..8f7a31481744 100644
--- a/drivers/media/dvb-frontends/stv0367.h
+++ b/drivers/media/dvb-frontends/stv0367.h
@@ -25,6 +25,9 @@
#include <linux/dvb/frontend.h>
#include "dvb_frontend.h"
+#define STV0367_ICSPEED_53125 53125000
+#define STV0367_ICSPEED_58000 58000000
+
struct stv0367_config {
u8 demod_address;
u32 xtal;
@@ -41,6 +44,9 @@ dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
extern struct
dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
struct i2c_adapter *i2c);
+extern struct
+dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c);
#else
static inline struct
dvb_frontend *stv0367ter_attach(const struct stv0367_config *config,
@@ -56,6 +62,13 @@ dvb_frontend *stv0367cab_attach(const struct stv0367_config *config,
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+static inline struct
+dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
#endif
#endif
diff --git a/drivers/media/dvb-frontends/stv0367_defs.h b/drivers/media/dvb-frontends/stv0367_defs.h
new file mode 100644
index 000000000000..277d2971ed3f
--- /dev/null
+++ b/drivers/media/dvb-frontends/stv0367_defs.h
@@ -0,0 +1,1301 @@
+/*
+ * stv0367_defs.h
+ *
+ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC.
+ *
+ * Copyright (C) ST Microelectronics.
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ */
+
+#ifndef STV0367_DEFS_H
+#define STV0367_DEFS_H
+
+#include "stv0367_regs.h"
+
+#define STV0367_DEFTAB_GENERIC 0
+#define STV0367_DEFTAB_DDB 1
+#define STV0367_DEFTAB_MAX 2
+
+#define STV0367_TAB_TER 0
+#define STV0367_TAB_CAB 1
+#define STV0367_TAB_BASE 2
+#define STV0367_TAB_MAX 3
+
+struct st_register {
+ u16 addr;
+ u8 value;
+};
+
+/* values for STV4100 XTAL=30M int clk=53.125M*/
+static const struct st_register def0367ter[] = {
+ {R367TER_ID, 0x60},
+ {R367TER_I2CRPT, 0xa0},
+ /* {R367TER_I2CRPT, 0x22},*/
+ {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */
+ {R367TER_IOCFG0, 0x40},
+ {R367TER_DAC0R, 0x00},
+ {R367TER_IOCFG1, 0x00},
+ {R367TER_DAC1R, 0x00},
+ {R367TER_IOCFG2, 0x62},
+ {R367TER_SDFR, 0x00},
+ {R367TER_STATUS, 0xf8},
+ {R367TER_AUX_CLK, 0x0a},
+ {R367TER_FREESYS1, 0x00},
+ {R367TER_FREESYS2, 0x00},
+ {R367TER_FREESYS3, 0x00},
+ {R367TER_GPIO_CFG, 0x55},
+ {R367TER_GPIO_CMD, 0x00},
+ {R367TER_AGC2MAX, 0xff},
+ {R367TER_AGC2MIN, 0x00},
+ {R367TER_AGC1MAX, 0xff},
+ {R367TER_AGC1MIN, 0x00},
+ {R367TER_AGCR, 0xbc},
+ {R367TER_AGC2TH, 0x00},
+ {R367TER_AGC12C, 0x00},
+ {R367TER_AGCCTRL1, 0x85},
+ {R367TER_AGCCTRL2, 0x1f},
+ {R367TER_AGC1VAL1, 0x00},
+ {R367TER_AGC1VAL2, 0x00},
+ {R367TER_AGC2VAL1, 0x6f},
+ {R367TER_AGC2VAL2, 0x05},
+ {R367TER_AGC2PGA, 0x00},
+ {R367TER_OVF_RATE1, 0x00},
+ {R367TER_OVF_RATE2, 0x00},
+ {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */
+ {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */
+ {R367TER_INC_DEROT1, 0x55},
+ {R367TER_INC_DEROT2, 0x55},
+ {R367TER_PPM_CPAMP_DIR, 0x2c},
+ {R367TER_PPM_CPAMP_INV, 0x00},
+ {R367TER_FREESTFE_1, 0x00},
+ {R367TER_FREESTFE_2, 0x1c},
+ {R367TER_DCOFFSET, 0x00},
+ {R367TER_EN_PROCESS, 0x05},
+ {R367TER_SDI_SMOOTHER, 0x80},
+ {R367TER_FE_LOOP_OPEN, 0x1c},
+ {R367TER_FREQOFF1, 0x00},
+ {R367TER_FREQOFF2, 0x00},
+ {R367TER_FREQOFF3, 0x00},
+ {R367TER_TIMOFF1, 0x00},
+ {R367TER_TIMOFF2, 0x00},
+ {R367TER_EPQ, 0x02},
+ {R367TER_EPQAUTO, 0x01},
+ {R367TER_SYR_UPDATE, 0xf5},
+ {R367TER_CHPFREE, 0x00},
+ {R367TER_PPM_STATE_MAC, 0x23},
+ {R367TER_INR_THRESHOLD, 0xff},
+ {R367TER_EPQ_TPS_ID_CELL, 0xf9},
+ {R367TER_EPQ_CFG, 0x00},
+ {R367TER_EPQ_STATUS, 0x01},
+ {R367TER_AUTORELOCK, 0x81},
+ {R367TER_BER_THR_VMSB, 0x00},
+ {R367TER_BER_THR_MSB, 0x00},
+ {R367TER_BER_THR_LSB, 0x00},
+ {R367TER_CCD, 0x83},
+ {R367TER_SPECTR_CFG, 0x00},
+ {R367TER_CHC_DUMMY, 0x18},
+ {R367TER_INC_CTL, 0x88},
+ {R367TER_INCTHRES_COR1, 0xb4},
+ {R367TER_INCTHRES_COR2, 0x96},
+ {R367TER_INCTHRES_DET1, 0x0e},
+ {R367TER_INCTHRES_DET2, 0x11},
+ {R367TER_IIR_CELLNB, 0x8d},
+ {R367TER_IIRCX_COEFF1_MSB, 0x00},
+ {R367TER_IIRCX_COEFF1_LSB, 0x00},
+ {R367TER_IIRCX_COEFF2_MSB, 0x09},
+ {R367TER_IIRCX_COEFF2_LSB, 0x18},
+ {R367TER_IIRCX_COEFF3_MSB, 0x14},
+ {R367TER_IIRCX_COEFF3_LSB, 0x9c},
+ {R367TER_IIRCX_COEFF4_MSB, 0x00},
+ {R367TER_IIRCX_COEFF4_LSB, 0x00},
+ {R367TER_IIRCX_COEFF5_MSB, 0x36},
+ {R367TER_IIRCX_COEFF5_LSB, 0x42},
+ {R367TER_FEPATH_CFG, 0x00},
+ {R367TER_PMC1_FUNC, 0x65},
+ {R367TER_PMC1_FOR, 0x00},
+ {R367TER_PMC2_FUNC, 0x00},
+ {R367TER_STATUS_ERR_DA, 0xe0},
+ {R367TER_DIG_AGC_R, 0xfe},
+ {R367TER_COMAGC_TARMSB, 0x0b},
+ {R367TER_COM_AGC_TAR_ENMODE, 0x41},
+ {R367TER_COM_AGC_CFG, 0x3e},
+ {R367TER_COM_AGC_GAIN1, 0x39},
+ {R367TER_AUT_AGC_TARGETMSB, 0x0b},
+ {R367TER_LOCK_DET_MSB, 0x01},
+ {R367TER_AGCTAR_LOCK_LSBS, 0x40},
+ {R367TER_AUT_GAIN_EN, 0xf4},
+ {R367TER_AUT_CFG, 0xf0},
+ {R367TER_LOCKN, 0x23},
+ {R367TER_INT_X_3, 0x00},
+ {R367TER_INT_X_2, 0x03},
+ {R367TER_INT_X_1, 0x8d},
+ {R367TER_INT_X_0, 0xa0},
+ {R367TER_MIN_ERRX_MSB, 0x00},
+ {R367TER_COR_CTL, 0x23},
+ {R367TER_COR_STAT, 0xf6},
+ {R367TER_COR_INTEN, 0x00},
+ {R367TER_COR_INTSTAT, 0x3f},
+ {R367TER_COR_MODEGUARD, 0x03},
+ {R367TER_AGC_CTL, 0x08},
+ {R367TER_AGC_MANUAL1, 0x00},
+ {R367TER_AGC_MANUAL2, 0x00},
+ {R367TER_AGC_TARG, 0x16},
+ {R367TER_AGC_GAIN1, 0x53},
+ {R367TER_AGC_GAIN2, 0x1d},
+ {R367TER_RESERVED_1, 0x00},
+ {R367TER_RESERVED_2, 0x00},
+ {R367TER_RESERVED_3, 0x00},
+ {R367TER_CAS_CTL, 0x44},
+ {R367TER_CAS_FREQ, 0xb3},
+ {R367TER_CAS_DAGCGAIN, 0x12},
+ {R367TER_SYR_CTL, 0x04},
+ {R367TER_SYR_STAT, 0x10},
+ {R367TER_SYR_NCO1, 0x00},
+ {R367TER_SYR_NCO2, 0x00},
+ {R367TER_SYR_OFFSET1, 0x00},
+ {R367TER_SYR_OFFSET2, 0x00},
+ {R367TER_FFT_CTL, 0x00},
+ {R367TER_SCR_CTL, 0x70},
+ {R367TER_PPM_CTL1, 0xf8},
+ {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */
+ {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */
+ {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */
+ {R367TER_TRL_TIME1, 0x1d},
+ {R367TER_TRL_TIME2, 0xfc},
+ {R367TER_CRL_CTL, 0x24},
+ {R367TER_CRL_FREQ1, 0xad},
+ {R367TER_CRL_FREQ2, 0x9d},
+ {R367TER_CRL_FREQ3, 0xff},
+ {R367TER_CHC_CTL, 0x01},
+ {R367TER_CHC_SNR, 0xf0},
+ {R367TER_BDI_CTL, 0x00},
+ {R367TER_DMP_CTL, 0x00},
+ {R367TER_TPS_RCVD1, 0x30},
+ {R367TER_TPS_RCVD2, 0x02},
+ {R367TER_TPS_RCVD3, 0x01},
+ {R367TER_TPS_RCVD4, 0x00},
+ {R367TER_TPS_ID_CELL1, 0x00},
+ {R367TER_TPS_ID_CELL2, 0x00},
+ {R367TER_TPS_RCVD5_SET1, 0x02},
+ {R367TER_TPS_SET2, 0x02},
+ {R367TER_TPS_SET3, 0x01},
+ {R367TER_TPS_CTL, 0x00},
+ {R367TER_CTL_FFTOSNUM, 0x34},
+ {R367TER_TESTSELECT, 0x09},
+ {R367TER_MSC_REV, 0x0a},
+ {R367TER_PIR_CTL, 0x00},
+ {R367TER_SNR_CARRIER1, 0xa1},
+ {R367TER_SNR_CARRIER2, 0x9a},
+ {R367TER_PPM_CPAMP, 0x2c},
+ {R367TER_TSM_AP0, 0x00},
+ {R367TER_TSM_AP1, 0x00},
+ {R367TER_TSM_AP2, 0x00},
+ {R367TER_TSM_AP3, 0x00},
+ {R367TER_TSM_AP4, 0x00},
+ {R367TER_TSM_AP5, 0x00},
+ {R367TER_TSM_AP6, 0x00},
+ {R367TER_TSM_AP7, 0x00},
+ {R367TER_TSTRES, 0x00},
+ {R367TER_ANACTRL, 0x0D},/* PLL stopped, restart at init!!! */
+ {R367TER_TSTBUS, 0x00},
+ {R367TER_TSTRATE, 0x00},
+ {R367TER_CONSTMODE, 0x01},
+ {R367TER_CONSTCARR1, 0x00},
+ {R367TER_CONSTCARR2, 0x00},
+ {R367TER_ICONSTEL, 0x0a},
+ {R367TER_QCONSTEL, 0x15},
+ {R367TER_TSTBISTRES0, 0x00},
+ {R367TER_TSTBISTRES1, 0x00},
+ {R367TER_TSTBISTRES2, 0x28},
+ {R367TER_TSTBISTRES3, 0x00},
+ {R367TER_RF_AGC1, 0xff},
+ {R367TER_RF_AGC2, 0x83},
+ {R367TER_ANADIGCTRL, 0x19},
+ {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */
+ {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */
+ {R367TER_PLLSETUP, 0x18},
+ {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */
+ {R367TER_TSTBIST, 0x00},
+ {R367TER_PAD_COMP_CTRL, 0x00},
+ {R367TER_PAD_COMP_WR, 0x00},
+ {R367TER_PAD_COMP_RD, 0xe0},
+ {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00},
+ {R367TER_SYR_FLAG, 0x00},
+ {R367TER_CRL_TARGET1, 0x00},
+ {R367TER_CRL_TARGET2, 0x00},
+ {R367TER_CRL_TARGET3, 0x00},
+ {R367TER_CRL_TARGET4, 0x00},
+ {R367TER_CRL_FLAG, 0x00},
+ {R367TER_TRL_TARGET1, 0x00},
+ {R367TER_TRL_TARGET2, 0x00},
+ {R367TER_TRL_CHC, 0x00},
+ {R367TER_CHC_SNR_TARG, 0x00},
+ {R367TER_TOP_TRACK, 0x00},
+ {R367TER_TRACKER_FREE1, 0x00},
+ {R367TER_ERROR_CRL1, 0x00},
+ {R367TER_ERROR_CRL2, 0x00},
+ {R367TER_ERROR_CRL3, 0x00},
+ {R367TER_ERROR_CRL4, 0x00},
+ {R367TER_DEC_NCO1, 0x2c},
+ {R367TER_DEC_NCO2, 0x0f},
+ {R367TER_DEC_NCO3, 0x20},
+ {R367TER_SNR, 0xf1},
+ {R367TER_SYR_FFTADJ1, 0x00},
+ {R367TER_SYR_FFTADJ2, 0x00},
+ {R367TER_SYR_CHCADJ1, 0x00},
+ {R367TER_SYR_CHCADJ2, 0x00},
+ {R367TER_SYR_OFF, 0x00},
+ {R367TER_PPM_OFFSET1, 0x00},
+ {R367TER_PPM_OFFSET2, 0x03},
+ {R367TER_TRACKER_FREE2, 0x00},
+ {R367TER_DEBG_LT10, 0x00},
+ {R367TER_DEBG_LT11, 0x00},
+ {R367TER_DEBG_LT12, 0x00},
+ {R367TER_DEBG_LT13, 0x00},
+ {R367TER_DEBG_LT14, 0x00},
+ {R367TER_DEBG_LT15, 0x00},
+ {R367TER_DEBG_LT16, 0x00},
+ {R367TER_DEBG_LT17, 0x00},
+ {R367TER_DEBG_LT18, 0x00},
+ {R367TER_DEBG_LT19, 0x00},
+ {R367TER_DEBG_LT1A, 0x00},
+ {R367TER_DEBG_LT1B, 0x00},
+ {R367TER_DEBG_LT1C, 0x00},
+ {R367TER_DEBG_LT1D, 0x00},
+ {R367TER_DEBG_LT1E, 0x00},
+ {R367TER_DEBG_LT1F, 0x00},
+ {R367TER_RCCFGH, 0x00},
+ {R367TER_RCCFGM, 0x00},
+ {R367TER_RCCFGL, 0x00},
+ {R367TER_RCINSDELH, 0x00},
+ {R367TER_RCINSDELM, 0x00},
+ {R367TER_RCINSDELL, 0x00},
+ {R367TER_RCSTATUS, 0x00},
+ {R367TER_RCSPEED, 0x6f},
+ {R367TER_RCDEBUGM, 0xe7},
+ {R367TER_RCDEBUGL, 0x9b},
+ {R367TER_RCOBSCFG, 0x00},
+ {R367TER_RCOBSM, 0x00},
+ {R367TER_RCOBSL, 0x00},
+ {R367TER_RCFECSPY, 0x00},
+ {R367TER_RCFSPYCFG, 0x00},
+ {R367TER_RCFSPYDATA, 0x00},
+ {R367TER_RCFSPYOUT, 0x00},
+ {R367TER_RCFSTATUS, 0x00},
+ {R367TER_RCFGOODPACK, 0x00},
+ {R367TER_RCFPACKCNT, 0x00},
+ {R367TER_RCFSPYMISC, 0x00},
+ {R367TER_RCFBERCPT4, 0x00},
+ {R367TER_RCFBERCPT3, 0x00},
+ {R367TER_RCFBERCPT2, 0x00},
+ {R367TER_RCFBERCPT1, 0x00},
+ {R367TER_RCFBERCPT0, 0x00},
+ {R367TER_RCFBERERR2, 0x00},
+ {R367TER_RCFBERERR1, 0x00},
+ {R367TER_RCFBERERR0, 0x00},
+ {R367TER_RCFSTATESM, 0x00},
+ {R367TER_RCFSTATESL, 0x00},
+ {R367TER_RCFSPYBER, 0x00},
+ {R367TER_RCFSPYDISTM, 0x00},
+ {R367TER_RCFSPYDISTL, 0x00},
+ {R367TER_RCFSPYOBS7, 0x00},
+ {R367TER_RCFSPYOBS6, 0x00},
+ {R367TER_RCFSPYOBS5, 0x00},
+ {R367TER_RCFSPYOBS4, 0x00},
+ {R367TER_RCFSPYOBS3, 0x00},
+ {R367TER_RCFSPYOBS2, 0x00},
+ {R367TER_RCFSPYOBS1, 0x00},
+ {R367TER_RCFSPYOBS0, 0x00},
+ {R367TER_TSGENERAL, 0x00},
+ {R367TER_RC1SPEED, 0x6f},
+ {R367TER_TSGSTATUS, 0x18},
+ {R367TER_FECM, 0x01},
+ {R367TER_VTH12, 0xff},
+ {R367TER_VTH23, 0xa1},
+ {R367TER_VTH34, 0x64},
+ {R367TER_VTH56, 0x40},
+ {R367TER_VTH67, 0x00},
+ {R367TER_VTH78, 0x2c},
+ {R367TER_VITCURPUN, 0x12},
+ {R367TER_VERROR, 0x01},
+ {R367TER_PRVIT, 0x3f},
+ {R367TER_VAVSRVIT, 0x00},
+ {R367TER_VSTATUSVIT, 0xbd},
+ {R367TER_VTHINUSE, 0xa1},
+ {R367TER_KDIV12, 0x20},
+ {R367TER_KDIV23, 0x40},
+ {R367TER_KDIV34, 0x20},
+ {R367TER_KDIV56, 0x30},
+ {R367TER_KDIV67, 0x00},
+ {R367TER_KDIV78, 0x30},
+ {R367TER_SIGPOWER, 0x54},
+ {R367TER_DEMAPVIT, 0x40},
+ {R367TER_VITSCALE, 0x00},
+ {R367TER_FFEC1PRG, 0x00},
+ {R367TER_FVITCURPUN, 0x12},
+ {R367TER_FVERROR, 0x01},
+ {R367TER_FVSTATUSVIT, 0xbd},
+ {R367TER_DEBUG_LT1, 0x00},
+ {R367TER_DEBUG_LT2, 0x00},
+ {R367TER_DEBUG_LT3, 0x00},
+ {R367TER_TSTSFMET, 0x00},
+ {R367TER_SELOUT, 0x00},
+ {R367TER_TSYNC, 0x00},
+ {R367TER_TSTERR, 0x00},
+ {R367TER_TSFSYNC, 0x00},
+ {R367TER_TSTSFERR, 0x00},
+ {R367TER_TSTTSSF1, 0x01},
+ {R367TER_TSTTSSF2, 0x1f},
+ {R367TER_TSTTSSF3, 0x00},
+ {R367TER_TSTTS1, 0x00},
+ {R367TER_TSTTS2, 0x1f},
+ {R367TER_TSTTS3, 0x01},
+ {R367TER_TSTTS4, 0x00},
+ {R367TER_TSTTSRC, 0x00},
+ {R367TER_TSTTSRS, 0x00},
+ {R367TER_TSSTATEM, 0xb0},
+ {R367TER_TSSTATEL, 0x40},
+ {R367TER_TSCFGH, 0xC0},
+ {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */
+ {R367TER_TSCFGL, 0x20},
+ {R367TER_TSSYNC, 0x00},
+ {R367TER_TSINSDELH, 0x00},
+ {R367TER_TSINSDELM, 0x00},
+ {R367TER_TSINSDELL, 0x00},
+ {R367TER_TSDIVN, 0x03},
+ {R367TER_TSDIVPM, 0x00},
+ {R367TER_TSDIVPL, 0x00},
+ {R367TER_TSDIVQM, 0x00},
+ {R367TER_TSDIVQL, 0x00},
+ {R367TER_TSDILSTKM, 0x00},
+ {R367TER_TSDILSTKL, 0x00},
+ {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */
+ {R367TER_TSSTATUS, 0x81},
+ {R367TER_TSSTATUS2, 0x6a},
+ {R367TER_TSBITRATEM, 0x0f},
+ {R367TER_TSBITRATEL, 0xc6},
+ {R367TER_TSPACKLENM, 0x00},
+ {R367TER_TSPACKLENL, 0xfc},
+ {R367TER_TSBLOCLENM, 0x0a},
+ {R367TER_TSBLOCLENL, 0x80},
+ {R367TER_TSDLYH, 0x90},
+ {R367TER_TSDLYM, 0x68},
+ {R367TER_TSDLYL, 0x01},
+ {R367TER_TSNPDAV, 0x00},
+ {R367TER_TSBUFSTATH, 0x00},
+ {R367TER_TSBUFSTATM, 0x00},
+ {R367TER_TSBUFSTATL, 0x00},
+ {R367TER_TSDEBUGM, 0xcf},
+ {R367TER_TSDEBUGL, 0x1e},
+ {R367TER_TSDLYSETH, 0x00},
+ {R367TER_TSDLYSETM, 0x68},
+ {R367TER_TSDLYSETL, 0x00},
+ {R367TER_TSOBSCFG, 0x00},
+ {R367TER_TSOBSM, 0x47},
+ {R367TER_TSOBSL, 0x1f},
+ {R367TER_ERRCTRL1, 0x95},
+ {R367TER_ERRCNT1H, 0x80},
+ {R367TER_ERRCNT1M, 0x00},
+ {R367TER_ERRCNT1L, 0x00},
+ {R367TER_ERRCTRL2, 0x95},
+ {R367TER_ERRCNT2H, 0x00},
+ {R367TER_ERRCNT2M, 0x00},
+ {R367TER_ERRCNT2L, 0x00},
+ {R367TER_FECSPY, 0x88},
+ {R367TER_FSPYCFG, 0x2c},
+ {R367TER_FSPYDATA, 0x3a},
+ {R367TER_FSPYOUT, 0x06},
+ {R367TER_FSTATUS, 0x61},
+ {R367TER_FGOODPACK, 0xff},
+ {R367TER_FPACKCNT, 0xff},
+ {R367TER_FSPYMISC, 0x66},
+ {R367TER_FBERCPT4, 0x00},
+ {R367TER_FBERCPT3, 0x00},
+ {R367TER_FBERCPT2, 0x36},
+ {R367TER_FBERCPT1, 0x36},
+ {R367TER_FBERCPT0, 0x14},
+ {R367TER_FBERERR2, 0x00},
+ {R367TER_FBERERR1, 0x03},
+ {R367TER_FBERERR0, 0x28},
+ {R367TER_FSTATESM, 0x00},
+ {R367TER_FSTATESL, 0x02},
+ {R367TER_FSPYBER, 0x00},
+ {R367TER_FSPYDISTM, 0x01},
+ {R367TER_FSPYDISTL, 0x9f},
+ {R367TER_FSPYOBS7, 0xc9},
+ {R367TER_FSPYOBS6, 0x99},
+ {R367TER_FSPYOBS5, 0x08},
+ {R367TER_FSPYOBS4, 0xec},
+ {R367TER_FSPYOBS3, 0x01},
+ {R367TER_FSPYOBS2, 0x0f},
+ {R367TER_FSPYOBS1, 0xf5},
+ {R367TER_FSPYOBS0, 0x08},
+ {R367TER_SFDEMAP, 0x40},
+ {R367TER_SFERROR, 0x00},
+ {R367TER_SFAVSR, 0x30},
+ {R367TER_SFECSTATUS, 0xcc},
+ {R367TER_SFKDIV12, 0x20},
+ {R367TER_SFKDIV23, 0x40},
+ {R367TER_SFKDIV34, 0x20},
+ {R367TER_SFKDIV56, 0x20},
+ {R367TER_SFKDIV67, 0x00},
+ {R367TER_SFKDIV78, 0x20},
+ {R367TER_SFDILSTKM, 0x00},
+ {R367TER_SFDILSTKL, 0x00},
+ {R367TER_SFSTATUS, 0xb5},
+ {R367TER_SFDLYH, 0x90},
+ {R367TER_SFDLYM, 0x60},
+ {R367TER_SFDLYL, 0x01},
+ {R367TER_SFDLYSETH, 0xc0},
+ {R367TER_SFDLYSETM, 0x60},
+ {R367TER_SFDLYSETL, 0x00},
+ {R367TER_SFOBSCFG, 0x00},
+ {R367TER_SFOBSM, 0x47},
+ {R367TER_SFOBSL, 0x05},
+ {R367TER_SFECINFO, 0x40},
+ {R367TER_SFERRCTRL, 0x74},
+ {R367TER_SFERRCNTH, 0x80},
+ {R367TER_SFERRCNTM, 0x00},
+ {R367TER_SFERRCNTL, 0x00},
+ {R367TER_SYMBRATEM, 0x2f},
+ {R367TER_SYMBRATEL, 0x50},
+ {R367TER_SYMBSTATUS, 0x7f},
+ {R367TER_SYMBCFG, 0x00},
+ {R367TER_SYMBFIFOM, 0xf4},
+ {R367TER_SYMBFIFOL, 0x0d},
+ {R367TER_SYMBOFFSM, 0xf0},
+ {R367TER_SYMBOFFSL, 0x2d},
+ {R367TER_DEBUG_LT4, 0x00},
+ {R367TER_DEBUG_LT5, 0x00},
+ {R367TER_DEBUG_LT6, 0x00},
+ {R367TER_DEBUG_LT7, 0x00},
+ {R367TER_DEBUG_LT8, 0x00},
+ {R367TER_DEBUG_LT9, 0x00},
+ {0x0000, 0x00},
+};
+
+static const struct st_register def0367cab[] = {
+ {R367CAB_ID, 0x60},
+ {R367CAB_I2CRPT, 0xa0},
+ /*{R367CAB_I2CRPT, 0x22},*/
+ {R367CAB_TOPCTRL, 0x10},
+ {R367CAB_IOCFG0, 0x80},
+ {R367CAB_DAC0R, 0x00},
+ {R367CAB_IOCFG1, 0x00},
+ {R367CAB_DAC1R, 0x00},
+ {R367CAB_IOCFG2, 0x00},
+ {R367CAB_SDFR, 0x00},
+ {R367CAB_AUX_CLK, 0x00},
+ {R367CAB_FREESYS1, 0x00},
+ {R367CAB_FREESYS2, 0x00},
+ {R367CAB_FREESYS3, 0x00},
+ {R367CAB_GPIO_CFG, 0x55},
+ {R367CAB_GPIO_CMD, 0x01},
+ {R367CAB_TSTRES, 0x00},
+ {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/
+ {R367CAB_TSTBUS, 0x00},
+ {R367CAB_RF_AGC1, 0xea},
+ {R367CAB_RF_AGC2, 0x82},
+ {R367CAB_ANADIGCTRL, 0x0b},
+ {R367CAB_PLLMDIV, 0x01},
+ {R367CAB_PLLNDIV, 0x08},
+ {R367CAB_PLLSETUP, 0x18},
+ {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */
+ {R367CAB_TSTBIST, 0x00},
+ {R367CAB_CTRL_1, 0x00},
+ {R367CAB_CTRL_2, 0x03},
+ {R367CAB_IT_STATUS1, 0x2b},
+ {R367CAB_IT_STATUS2, 0x08},
+ {R367CAB_IT_EN1, 0x00},
+ {R367CAB_IT_EN2, 0x00},
+ {R367CAB_CTRL_STATUS, 0x04},
+ {R367CAB_TEST_CTL, 0x00},
+ {R367CAB_AGC_CTL, 0x73},
+ {R367CAB_AGC_IF_CFG, 0x50},
+ {R367CAB_AGC_RF_CFG, 0x00},
+ {R367CAB_AGC_PWM_CFG, 0x03},
+ {R367CAB_AGC_PWR_REF_L, 0x5a},
+ {R367CAB_AGC_PWR_REF_H, 0x00},
+ {R367CAB_AGC_RF_TH_L, 0xff},
+ {R367CAB_AGC_RF_TH_H, 0x07},
+ {R367CAB_AGC_IF_LTH_L, 0x00},
+ {R367CAB_AGC_IF_LTH_H, 0x08},
+ {R367CAB_AGC_IF_HTH_L, 0xff},
+ {R367CAB_AGC_IF_HTH_H, 0x07},
+ {R367CAB_AGC_PWR_RD_L, 0xa0},
+ {R367CAB_AGC_PWR_RD_M, 0xe9},
+ {R367CAB_AGC_PWR_RD_H, 0x03},
+ {R367CAB_AGC_PWM_IFCMD_L, 0xe4},
+ {R367CAB_AGC_PWM_IFCMD_H, 0x00},
+ {R367CAB_AGC_PWM_RFCMD_L, 0xff},
+ {R367CAB_AGC_PWM_RFCMD_H, 0x07},
+ {R367CAB_IQDEM_CFG, 0x01},
+ {R367CAB_MIX_NCO_LL, 0x22},
+ {R367CAB_MIX_NCO_HL, 0x96},
+ {R367CAB_MIX_NCO_HH, 0x55},
+ {R367CAB_SRC_NCO_LL, 0xff},
+ {R367CAB_SRC_NCO_LH, 0x0c},
+ {R367CAB_SRC_NCO_HL, 0xf5},
+ {R367CAB_SRC_NCO_HH, 0x20},
+ {R367CAB_IQDEM_GAIN_SRC_L, 0x06},
+ {R367CAB_IQDEM_GAIN_SRC_H, 0x01},
+ {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe},
+ {R367CAB_IQDEM_DCRM_CFG_LH, 0xff},
+ {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f},
+ {R367CAB_IQDEM_DCRM_CFG_HH, 0x00},
+ {R367CAB_IQDEM_ADJ_COEFF0, 0x34},
+ {R367CAB_IQDEM_ADJ_COEFF1, 0xae},
+ {R367CAB_IQDEM_ADJ_COEFF2, 0x46},
+ {R367CAB_IQDEM_ADJ_COEFF3, 0x77},
+ {R367CAB_IQDEM_ADJ_COEFF4, 0x96},
+ {R367CAB_IQDEM_ADJ_COEFF5, 0x69},
+ {R367CAB_IQDEM_ADJ_COEFF6, 0xc7},
+ {R367CAB_IQDEM_ADJ_COEFF7, 0x01},
+ {R367CAB_IQDEM_ADJ_EN, 0x04},
+ {R367CAB_IQDEM_ADJ_AGC_REF, 0x94},
+ {R367CAB_ALLPASSFILT1, 0xc9},
+ {R367CAB_ALLPASSFILT2, 0x2d},
+ {R367CAB_ALLPASSFILT3, 0xa3},
+ {R367CAB_ALLPASSFILT4, 0xfb},
+ {R367CAB_ALLPASSFILT5, 0xf6},
+ {R367CAB_ALLPASSFILT6, 0x45},
+ {R367CAB_ALLPASSFILT7, 0x6f},
+ {R367CAB_ALLPASSFILT8, 0x7e},
+ {R367CAB_ALLPASSFILT9, 0x05},
+ {R367CAB_ALLPASSFILT10, 0x0a},
+ {R367CAB_ALLPASSFILT11, 0x51},
+ {R367CAB_TRL_AGC_CFG, 0x20},
+ {R367CAB_TRL_LPF_CFG, 0x28},
+ {R367CAB_TRL_LPF_ACQ_GAIN, 0x44},
+ {R367CAB_TRL_LPF_TRK_GAIN, 0x22},
+ {R367CAB_TRL_LPF_OUT_GAIN, 0x03},
+ {R367CAB_TRL_LOCKDET_LTH, 0x04},
+ {R367CAB_TRL_LOCKDET_HTH, 0x11},
+ {R367CAB_TRL_LOCKDET_TRGVAL, 0x20},
+ {R367CAB_IQ_QAM, 0x01},
+ {R367CAB_FSM_STATE, 0xa0},
+ {R367CAB_FSM_CTL, 0x08},
+ {R367CAB_FSM_STS, 0x0c},
+ {R367CAB_FSM_SNR0_HTH, 0x00},
+ {R367CAB_FSM_SNR1_HTH, 0x00},
+ {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */
+ {R367CAB_FSM_SNR0_LTH, 0x00},
+ {R367CAB_FSM_SNR1_LTH, 0x00},
+ {R367CAB_FSM_EQA1_HTH, 0x00},
+ {R367CAB_FSM_TEMPO, 0x32},
+ {R367CAB_FSM_CONFIG, 0x03},
+ {R367CAB_EQU_I_TESTTAP_L, 0x11},
+ {R367CAB_EQU_I_TESTTAP_M, 0x00},
+ {R367CAB_EQU_I_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TESTAP_CFG, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_L, 0xff},
+ {R367CAB_EQU_Q_TESTTAP_M, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TAP_CTRL, 0x00},
+ {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11},
+ {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05},
+ {R367CAB_EQU_CTR_HIPOW_L, 0x00},
+ {R367CAB_EQU_CTR_HIPOW_H, 0x00},
+ {R367CAB_EQU_I_EQU_LO, 0xef},
+ {R367CAB_EQU_I_EQU_HI, 0x00},
+ {R367CAB_EQU_Q_EQU_LO, 0xee},
+ {R367CAB_EQU_Q_EQU_HI, 0x00},
+ {R367CAB_EQU_MAPPER, 0xc5},
+ {R367CAB_EQU_SWEEP_RATE, 0x80},
+ {R367CAB_EQU_SNR_LO, 0x64},
+ {R367CAB_EQU_SNR_HI, 0x03},
+ {R367CAB_EQU_GAMMA_LO, 0x00},
+ {R367CAB_EQU_GAMMA_HI, 0x00},
+ {R367CAB_EQU_ERR_GAIN, 0x36},
+ {R367CAB_EQU_RADIUS, 0xaa},
+ {R367CAB_EQU_FFE_MAINTAP, 0x00},
+ {R367CAB_EQU_FFE_LEAKAGE, 0x63},
+ {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf},
+ {R367CAB_EQU_GAIN_WIDE, 0x88},
+ {R367CAB_EQU_GAIN_NARROW, 0x41},
+ {R367CAB_EQU_CTR_LPF_GAIN, 0xd1},
+ {R367CAB_EQU_CRL_LPF_GAIN, 0xa7},
+ {R367CAB_EQU_GLOBAL_GAIN, 0x06},
+ {R367CAB_EQU_CRL_LD_SEN, 0x85},
+ {R367CAB_EQU_CRL_LD_VAL, 0xe2},
+ {R367CAB_EQU_CRL_TFR, 0x20},
+ {R367CAB_EQU_CRL_BISTH_LO, 0x00},
+ {R367CAB_EQU_CRL_BISTH_HI, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_LO, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_HI, 0x00},
+ {R367CAB_EQU_CRL_LIMITER, 0x40},
+ {R367CAB_EQU_MODULUS_MAP, 0x90},
+ {R367CAB_EQU_PNT_GAIN, 0xa7},
+ {R367CAB_FEC_AC_CTR_0, 0x16},
+ {R367CAB_FEC_AC_CTR_1, 0x0b},
+ {R367CAB_FEC_AC_CTR_2, 0x88},
+ {R367CAB_FEC_AC_CTR_3, 0x02},
+ {R367CAB_FEC_STATUS, 0x12},
+ {R367CAB_RS_COUNTER_0, 0x7d},
+ {R367CAB_RS_COUNTER_1, 0xd0},
+ {R367CAB_RS_COUNTER_2, 0x19},
+ {R367CAB_RS_COUNTER_3, 0x0b},
+ {R367CAB_RS_COUNTER_4, 0xa3},
+ {R367CAB_RS_COUNTER_5, 0x00},
+ {R367CAB_BERT_0, 0x01},
+ {R367CAB_BERT_1, 0x25},
+ {R367CAB_BERT_2, 0x41},
+ {R367CAB_BERT_3, 0x39},
+ {R367CAB_OUTFORMAT_0, 0xc2},
+ {R367CAB_OUTFORMAT_1, 0x22},
+ {R367CAB_SMOOTHER_2, 0x28},
+ {R367CAB_TSMF_CTRL_0, 0x01},
+ {R367CAB_TSMF_CTRL_1, 0xc6},
+ {R367CAB_TSMF_CTRL_3, 0x43},
+ {R367CAB_TS_ON_ID_0, 0x00},
+ {R367CAB_TS_ON_ID_1, 0x00},
+ {R367CAB_TS_ON_ID_2, 0x00},
+ {R367CAB_TS_ON_ID_3, 0x00},
+ {R367CAB_RE_STATUS_0, 0x00},
+ {R367CAB_RE_STATUS_1, 0x00},
+ {R367CAB_RE_STATUS_2, 0x00},
+ {R367CAB_RE_STATUS_3, 0x00},
+ {R367CAB_TS_STATUS_0, 0x00},
+ {R367CAB_TS_STATUS_1, 0x00},
+ {R367CAB_TS_STATUS_2, 0xa0},
+ {R367CAB_TS_STATUS_3, 0x00},
+ {R367CAB_T_O_ID_0, 0x00},
+ {R367CAB_T_O_ID_1, 0x00},
+ {R367CAB_T_O_ID_2, 0x00},
+ {R367CAB_T_O_ID_3, 0x00},
+ {0x0000, 0x00},
+};
+
+/**************
+ *
+ * Defaults / Tables for Digital Devices C/T Cine/Flex devices
+ *
+ **************/
+
+static const struct st_register def0367dd_ofdm[] = {
+ {R367TER_AGC2MAX, 0xff},
+ {R367TER_AGC2MIN, 0x00},
+ {R367TER_AGC1MAX, 0xff},
+ {R367TER_AGC1MIN, 0x00},
+ {R367TER_AGCR, 0xbc},
+ {R367TER_AGC2TH, 0x00},
+ {R367TER_AGCCTRL1, 0x85},
+ {R367TER_AGCCTRL2, 0x1f},
+ {R367TER_AGC1VAL1, 0x00},
+ {R367TER_AGC1VAL2, 0x00},
+ {R367TER_AGC2VAL1, 0x6f},
+ {R367TER_AGC2VAL2, 0x05},
+ {R367TER_AGC2PGA, 0x00},
+ {R367TER_OVF_RATE1, 0x00},
+ {R367TER_OVF_RATE2, 0x00},
+ {R367TER_GAIN_SRC1, 0x2b},
+ {R367TER_GAIN_SRC2, 0x04},
+ {R367TER_INC_DEROT1, 0x55},
+ {R367TER_INC_DEROT2, 0x55},
+ {R367TER_PPM_CPAMP_DIR, 0x2c},
+ {R367TER_PPM_CPAMP_INV, 0x00},
+ {R367TER_FREESTFE_1, 0x00},
+ {R367TER_FREESTFE_2, 0x1c},
+ {R367TER_DCOFFSET, 0x00},
+ {R367TER_EN_PROCESS, 0x05},
+ {R367TER_SDI_SMOOTHER, 0x80},
+ {R367TER_FE_LOOP_OPEN, 0x1c},
+ {R367TER_FREQOFF1, 0x00},
+ {R367TER_FREQOFF2, 0x00},
+ {R367TER_FREQOFF3, 0x00},
+ {R367TER_TIMOFF1, 0x00},
+ {R367TER_TIMOFF2, 0x00},
+ {R367TER_EPQ, 0x02},
+ {R367TER_EPQAUTO, 0x01},
+ {R367TER_SYR_UPDATE, 0xf5},
+ {R367TER_CHPFREE, 0x00},
+ {R367TER_PPM_STATE_MAC, 0x23},
+ {R367TER_INR_THRESHOLD, 0xff},
+ {R367TER_EPQ_TPS_ID_CELL, 0xf9},
+ {R367TER_EPQ_CFG, 0x00},
+ {R367TER_EPQ_STATUS, 0x01},
+ {R367TER_AUTORELOCK, 0x81},
+ {R367TER_BER_THR_VMSB, 0x00},
+ {R367TER_BER_THR_MSB, 0x00},
+ {R367TER_BER_THR_LSB, 0x00},
+ {R367TER_CCD, 0x83},
+ {R367TER_SPECTR_CFG, 0x00},
+ {R367TER_CHC_DUMMY, 0x18},
+ {R367TER_INC_CTL, 0x88},
+ {R367TER_INCTHRES_COR1, 0xb4},
+ {R367TER_INCTHRES_COR2, 0x96},
+ {R367TER_INCTHRES_DET1, 0x0e},
+ {R367TER_INCTHRES_DET2, 0x11},
+ {R367TER_IIR_CELLNB, 0x8d},
+ {R367TER_IIRCX_COEFF1_MSB, 0x00},
+ {R367TER_IIRCX_COEFF1_LSB, 0x00},
+ {R367TER_IIRCX_COEFF2_MSB, 0x09},
+ {R367TER_IIRCX_COEFF2_LSB, 0x18},
+ {R367TER_IIRCX_COEFF3_MSB, 0x14},
+ {R367TER_IIRCX_COEFF3_LSB, 0x9c},
+ {R367TER_IIRCX_COEFF4_MSB, 0x00},
+ {R367TER_IIRCX_COEFF4_LSB, 0x00},
+ {R367TER_IIRCX_COEFF5_MSB, 0x36},
+ {R367TER_IIRCX_COEFF5_LSB, 0x42},
+ {R367TER_FEPATH_CFG, 0x00},
+ {R367TER_PMC1_FUNC, 0x65},
+ {R367TER_PMC1_FOR, 0x00},
+ {R367TER_PMC2_FUNC, 0x00},
+ {R367TER_STATUS_ERR_DA, 0xe0},
+ {R367TER_DIG_AGC_R, 0xfe},
+ {R367TER_COMAGC_TARMSB, 0x0b},
+ {R367TER_COM_AGC_TAR_ENMODE, 0x41},
+ {R367TER_COM_AGC_CFG, 0x3e},
+ {R367TER_COM_AGC_GAIN1, 0x39},
+ {R367TER_AUT_AGC_TARGETMSB, 0x0b},
+ {R367TER_LOCK_DET_MSB, 0x01},
+ {R367TER_AGCTAR_LOCK_LSBS, 0x40},
+ {R367TER_AUT_GAIN_EN, 0xf4},
+ {R367TER_AUT_CFG, 0xf0},
+ {R367TER_LOCKN, 0x23},
+ {R367TER_INT_X_3, 0x00},
+ {R367TER_INT_X_2, 0x03},
+ {R367TER_INT_X_1, 0x8d},
+ {R367TER_INT_X_0, 0xa0},
+ {R367TER_MIN_ERRX_MSB, 0x00},
+ {R367TER_COR_CTL, 0x00},
+ {R367TER_COR_STAT, 0xf6},
+ {R367TER_COR_INTEN, 0x00},
+ {R367TER_COR_INTSTAT, 0x3f},
+ {R367TER_COR_MODEGUARD, 0x03},
+ {R367TER_AGC_CTL, 0x08},
+ {R367TER_AGC_MANUAL1, 0x00},
+ {R367TER_AGC_MANUAL2, 0x00},
+ {R367TER_AGC_TARG, 0x16},
+ {R367TER_AGC_GAIN1, 0x53},
+ {R367TER_AGC_GAIN2, 0x1d},
+ {R367TER_RESERVED_1, 0x00},
+ {R367TER_RESERVED_2, 0x00},
+ {R367TER_RESERVED_3, 0x00},
+ {R367TER_CAS_CTL, 0x44},
+ {R367TER_CAS_FREQ, 0xb3},
+ {R367TER_CAS_DAGCGAIN, 0x12},
+ {R367TER_SYR_CTL, 0x04},
+ {R367TER_SYR_STAT, 0x10},
+ {R367TER_SYR_NCO1, 0x00},
+ {R367TER_SYR_NCO2, 0x00},
+ {R367TER_SYR_OFFSET1, 0x00},
+ {R367TER_SYR_OFFSET2, 0x00},
+ {R367TER_FFT_CTL, 0x00},
+ {R367TER_SCR_CTL, 0x70},
+ {R367TER_PPM_CTL1, 0xf8},
+ {R367TER_TRL_CTL, 0xac},
+ {R367TER_TRL_NOMRATE1, 0x1e},
+ {R367TER_TRL_NOMRATE2, 0x58},
+ {R367TER_TRL_TIME1, 0x1d},
+ {R367TER_TRL_TIME2, 0xfc},
+ {R367TER_CRL_CTL, 0x24},
+ {R367TER_CRL_FREQ1, 0xad},
+ {R367TER_CRL_FREQ2, 0x9d},
+ {R367TER_CRL_FREQ3, 0xff},
+ {R367TER_CHC_CTL, 0x01},
+ {R367TER_CHC_SNR, 0xf0},
+ {R367TER_BDI_CTL, 0x00},
+ {R367TER_DMP_CTL, 0x00},
+ {R367TER_TPS_RCVD1, 0x30},
+ {R367TER_TPS_RCVD2, 0x02},
+ {R367TER_TPS_RCVD3, 0x01},
+ {R367TER_TPS_RCVD4, 0x00},
+ {R367TER_TPS_ID_CELL1, 0x00},
+ {R367TER_TPS_ID_CELL2, 0x00},
+ {R367TER_TPS_RCVD5_SET1, 0x02},
+ {R367TER_TPS_SET2, 0x02},
+ {R367TER_TPS_SET3, 0x01},
+ {R367TER_TPS_CTL, 0x00},
+ {R367TER_CTL_FFTOSNUM, 0x34},
+ {R367TER_TESTSELECT, 0x09},
+ {R367TER_MSC_REV, 0x0a},
+ {R367TER_PIR_CTL, 0x00},
+ {R367TER_SNR_CARRIER1, 0xa1},
+ {R367TER_SNR_CARRIER2, 0x9a},
+ {R367TER_PPM_CPAMP, 0x2c},
+ {R367TER_TSM_AP0, 0x00},
+ {R367TER_TSM_AP1, 0x00},
+ {R367TER_TSM_AP2, 0x00},
+ {R367TER_TSM_AP3, 0x00},
+ {R367TER_TSM_AP4, 0x00},
+ {R367TER_TSM_AP5, 0x00},
+ {R367TER_TSM_AP6, 0x00},
+ {R367TER_TSM_AP7, 0x00},
+ {R367TER_CONSTMODE, 0x01},
+ {R367TER_CONSTCARR1, 0x00},
+ {R367TER_CONSTCARR2, 0x00},
+ {R367TER_ICONSTEL, 0x0a},
+ {R367TER_QCONSTEL, 0x15},
+ {R367TER_TSTBISTRES0, 0x00},
+ {R367TER_TSTBISTRES1, 0x00},
+ {R367TER_TSTBISTRES2, 0x28},
+ {R367TER_TSTBISTRES3, 0x00},
+ {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00},
+ {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00},
+ {R367TER_SYR_FLAG, 0x00},
+ {R367TER_CRL_TARGET1, 0x00},
+ {R367TER_CRL_TARGET2, 0x00},
+ {R367TER_CRL_TARGET3, 0x00},
+ {R367TER_CRL_TARGET4, 0x00},
+ {R367TER_CRL_FLAG, 0x00},
+ {R367TER_TRL_TARGET1, 0x00},
+ {R367TER_TRL_TARGET2, 0x00},
+ {R367TER_TRL_CHC, 0x00},
+ {R367TER_CHC_SNR_TARG, 0x00},
+ {R367TER_TOP_TRACK, 0x00},
+ {R367TER_TRACKER_FREE1, 0x00},
+ {R367TER_ERROR_CRL1, 0x00},
+ {R367TER_ERROR_CRL2, 0x00},
+ {R367TER_ERROR_CRL3, 0x00},
+ {R367TER_ERROR_CRL4, 0x00},
+ {R367TER_DEC_NCO1, 0x2c},
+ {R367TER_DEC_NCO2, 0x0f},
+ {R367TER_DEC_NCO3, 0x20},
+ {R367TER_SNR, 0xf1},
+ {R367TER_SYR_FFTADJ1, 0x00},
+ {R367TER_SYR_FFTADJ2, 0x00},
+ {R367TER_SYR_CHCADJ1, 0x00},
+ {R367TER_SYR_CHCADJ2, 0x00},
+ {R367TER_SYR_OFF, 0x00},
+ {R367TER_PPM_OFFSET1, 0x00},
+ {R367TER_PPM_OFFSET2, 0x03},
+ {R367TER_TRACKER_FREE2, 0x00},
+ {R367TER_DEBG_LT10, 0x00},
+ {R367TER_DEBG_LT11, 0x00},
+ {R367TER_DEBG_LT12, 0x00},
+ {R367TER_DEBG_LT13, 0x00},
+ {R367TER_DEBG_LT14, 0x00},
+ {R367TER_DEBG_LT15, 0x00},
+ {R367TER_DEBG_LT16, 0x00},
+ {R367TER_DEBG_LT17, 0x00},
+ {R367TER_DEBG_LT18, 0x00},
+ {R367TER_DEBG_LT19, 0x00},
+ {R367TER_DEBG_LT1A, 0x00},
+ {R367TER_DEBG_LT1B, 0x00},
+ {R367TER_DEBG_LT1C, 0x00},
+ {R367TER_DEBG_LT1D, 0x00},
+ {R367TER_DEBG_LT1E, 0x00},
+ {R367TER_DEBG_LT1F, 0x00},
+ {R367TER_RCCFGH, 0x00},
+ {R367TER_RCCFGM, 0x00},
+ {R367TER_RCCFGL, 0x00},
+ {R367TER_RCINSDELH, 0x00},
+ {R367TER_RCINSDELM, 0x00},
+ {R367TER_RCINSDELL, 0x00},
+ {R367TER_RCSTATUS, 0x00},
+ {R367TER_RCSPEED, 0x6f},
+ {R367TER_RCDEBUGM, 0xe7},
+ {R367TER_RCDEBUGL, 0x9b},
+ {R367TER_RCOBSCFG, 0x00},
+ {R367TER_RCOBSM, 0x00},
+ {R367TER_RCOBSL, 0x00},
+ {R367TER_RCFECSPY, 0x00},
+ {R367TER_RCFSPYCFG, 0x00},
+ {R367TER_RCFSPYDATA, 0x00},
+ {R367TER_RCFSPYOUT, 0x00},
+ {R367TER_RCFSTATUS, 0x00},
+ {R367TER_RCFGOODPACK, 0x00},
+ {R367TER_RCFPACKCNT, 0x00},
+ {R367TER_RCFSPYMISC, 0x00},
+ {R367TER_RCFBERCPT4, 0x00},
+ {R367TER_RCFBERCPT3, 0x00},
+ {R367TER_RCFBERCPT2, 0x00},
+ {R367TER_RCFBERCPT1, 0x00},
+ {R367TER_RCFBERCPT0, 0x00},
+ {R367TER_RCFBERERR2, 0x00},
+ {R367TER_RCFBERERR1, 0x00},
+ {R367TER_RCFBERERR0, 0x00},
+ {R367TER_RCFSTATESM, 0x00},
+ {R367TER_RCFSTATESL, 0x00},
+ {R367TER_RCFSPYBER, 0x00},
+ {R367TER_RCFSPYDISTM, 0x00},
+ {R367TER_RCFSPYDISTL, 0x00},
+ {R367TER_RCFSPYOBS7, 0x00},
+ {R367TER_RCFSPYOBS6, 0x00},
+ {R367TER_RCFSPYOBS5, 0x00},
+ {R367TER_RCFSPYOBS4, 0x00},
+ {R367TER_RCFSPYOBS3, 0x00},
+ {R367TER_RCFSPYOBS2, 0x00},
+ {R367TER_RCFSPYOBS1, 0x00},
+ {R367TER_RCFSPYOBS0, 0x00},
+ {R367TER_FECM, 0x01},
+ {R367TER_VTH12, 0xff},
+ {R367TER_VTH23, 0xa1},
+ {R367TER_VTH34, 0x64},
+ {R367TER_VTH56, 0x40},
+ {R367TER_VTH67, 0x00},
+ {R367TER_VTH78, 0x2c},
+ {R367TER_VITCURPUN, 0x12},
+ {R367TER_VERROR, 0x01},
+ {R367TER_PRVIT, 0x3f},
+ {R367TER_VAVSRVIT, 0x00},
+ {R367TER_VSTATUSVIT, 0xbd},
+ {R367TER_VTHINUSE, 0xa1},
+ {R367TER_KDIV12, 0x20},
+ {R367TER_KDIV23, 0x40},
+ {R367TER_KDIV34, 0x20},
+ {R367TER_KDIV56, 0x30},
+ {R367TER_KDIV67, 0x00},
+ {R367TER_KDIV78, 0x30},
+ {R367TER_SIGPOWER, 0x54},
+ {R367TER_DEMAPVIT, 0x40},
+ {R367TER_VITSCALE, 0x00},
+ {R367TER_FFEC1PRG, 0x00},
+ {R367TER_FVITCURPUN, 0x12},
+ {R367TER_FVERROR, 0x01},
+ {R367TER_FVSTATUSVIT, 0xbd},
+ {R367TER_DEBUG_LT1, 0x00},
+ {R367TER_DEBUG_LT2, 0x00},
+ {R367TER_DEBUG_LT3, 0x00},
+ {R367TER_TSTSFMET, 0x00},
+ {R367TER_SELOUT, 0x00},
+ {R367TER_TSYNC, 0x00},
+ {R367TER_TSTERR, 0x00},
+ {R367TER_TSFSYNC, 0x00},
+ {R367TER_TSTSFERR, 0x00},
+ {R367TER_TSTTSSF1, 0x01},
+ {R367TER_TSTTSSF2, 0x1f},
+ {R367TER_TSTTSSF3, 0x00},
+ {R367TER_TSTTS1, 0x00},
+ {R367TER_TSTTS2, 0x1f},
+ {R367TER_TSTTS3, 0x01},
+ {R367TER_TSTTS4, 0x00},
+ {R367TER_TSTTSRC, 0x00},
+ {R367TER_TSTTSRS, 0x00},
+ {R367TER_TSSTATEM, 0xb0},
+ {R367TER_TSSTATEL, 0x40},
+ {R367TER_TSCFGH, 0x80},
+ {R367TER_TSCFGM, 0x00},
+ {R367TER_TSCFGL, 0x20},
+ {R367TER_TSSYNC, 0x00},
+ {R367TER_TSINSDELH, 0x00},
+ {R367TER_TSINSDELM, 0x00},
+ {R367TER_TSINSDELL, 0x00},
+ {R367TER_TSDIVN, 0x03},
+ {R367TER_TSDIVPM, 0x00},
+ {R367TER_TSDIVPL, 0x00},
+ {R367TER_TSDIVQM, 0x00},
+ {R367TER_TSDIVQL, 0x00},
+ {R367TER_TSDILSTKM, 0x00},
+ {R367TER_TSDILSTKL, 0x00},
+ {R367TER_TSSPEED, 0x6f},
+ {R367TER_TSSTATUS, 0x81},
+ {R367TER_TSSTATUS2, 0x6a},
+ {R367TER_TSBITRATEM, 0x0f},
+ {R367TER_TSBITRATEL, 0xc6},
+ {R367TER_TSPACKLENM, 0x00},
+ {R367TER_TSPACKLENL, 0xfc},
+ {R367TER_TSBLOCLENM, 0x0a},
+ {R367TER_TSBLOCLENL, 0x80},
+ {R367TER_TSDLYH, 0x90},
+ {R367TER_TSDLYM, 0x68},
+ {R367TER_TSDLYL, 0x01},
+ {R367TER_TSNPDAV, 0x00},
+ {R367TER_TSBUFSTATH, 0x00},
+ {R367TER_TSBUFSTATM, 0x00},
+ {R367TER_TSBUFSTATL, 0x00},
+ {R367TER_TSDEBUGM, 0xcf},
+ {R367TER_TSDEBUGL, 0x1e},
+ {R367TER_TSDLYSETH, 0x00},
+ {R367TER_TSDLYSETM, 0x68},
+ {R367TER_TSDLYSETL, 0x00},
+ {R367TER_TSOBSCFG, 0x00},
+ {R367TER_TSOBSM, 0x47},
+ {R367TER_TSOBSL, 0x1f},
+ {R367TER_ERRCTRL1, 0x95},
+ {R367TER_ERRCNT1H, 0x80},
+ {R367TER_ERRCNT1M, 0x00},
+ {R367TER_ERRCNT1L, 0x00},
+ {R367TER_ERRCTRL2, 0x95},
+ {R367TER_ERRCNT2H, 0x00},
+ {R367TER_ERRCNT2M, 0x00},
+ {R367TER_ERRCNT2L, 0x00},
+ {R367TER_FECSPY, 0x88},
+ {R367TER_FSPYCFG, 0x2c},
+ {R367TER_FSPYDATA, 0x3a},
+ {R367TER_FSPYOUT, 0x06},
+ {R367TER_FSTATUS, 0x61},
+ {R367TER_FGOODPACK, 0xff},
+ {R367TER_FPACKCNT, 0xff},
+ {R367TER_FSPYMISC, 0x66},
+ {R367TER_FBERCPT4, 0x00},
+ {R367TER_FBERCPT3, 0x00},
+ {R367TER_FBERCPT2, 0x36},
+ {R367TER_FBERCPT1, 0x36},
+ {R367TER_FBERCPT0, 0x14},
+ {R367TER_FBERERR2, 0x00},
+ {R367TER_FBERERR1, 0x03},
+ {R367TER_FBERERR0, 0x28},
+ {R367TER_FSTATESM, 0x00},
+ {R367TER_FSTATESL, 0x02},
+ {R367TER_FSPYBER, 0x00},
+ {R367TER_FSPYDISTM, 0x01},
+ {R367TER_FSPYDISTL, 0x9f},
+ {R367TER_FSPYOBS7, 0xc9},
+ {R367TER_FSPYOBS6, 0x99},
+ {R367TER_FSPYOBS5, 0x08},
+ {R367TER_FSPYOBS4, 0xec},
+ {R367TER_FSPYOBS3, 0x01},
+ {R367TER_FSPYOBS2, 0x0f},
+ {R367TER_FSPYOBS1, 0xf5},
+ {R367TER_FSPYOBS0, 0x08},
+ {R367TER_SFDEMAP, 0x40},
+ {R367TER_SFERROR, 0x00},
+ {R367TER_SFAVSR, 0x30},
+ {R367TER_SFECSTATUS, 0xcc},
+ {R367TER_SFKDIV12, 0x20},
+ {R367TER_SFKDIV23, 0x40},
+ {R367TER_SFKDIV34, 0x20},
+ {R367TER_SFKDIV56, 0x20},
+ {R367TER_SFKDIV67, 0x00},
+ {R367TER_SFKDIV78, 0x20},
+ {R367TER_SFDILSTKM, 0x00},
+ {R367TER_SFDILSTKL, 0x00},
+ {R367TER_SFSTATUS, 0xb5},
+ {R367TER_SFDLYH, 0x90},
+ {R367TER_SFDLYM, 0x60},
+ {R367TER_SFDLYL, 0x01},
+ {R367TER_SFDLYSETH, 0xc0},
+ {R367TER_SFDLYSETM, 0x60},
+ {R367TER_SFDLYSETL, 0x00},
+ {R367TER_SFOBSCFG, 0x00},
+ {R367TER_SFOBSM, 0x47},
+ {R367TER_SFOBSL, 0x05},
+ {R367TER_SFECINFO, 0x40},
+ {R367TER_SFERRCTRL, 0x74},
+ {R367TER_SFERRCNTH, 0x80},
+ {R367TER_SFERRCNTM, 0x00},
+ {R367TER_SFERRCNTL, 0x00},
+ {R367TER_SYMBRATEM, 0x2f},
+ {R367TER_SYMBRATEL, 0x50},
+ {R367TER_SYMBSTATUS, 0x7f},
+ {R367TER_SYMBCFG, 0x00},
+ {R367TER_SYMBFIFOM, 0xf4},
+ {R367TER_SYMBFIFOL, 0x0d},
+ {R367TER_SYMBOFFSM, 0xf0},
+ {R367TER_SYMBOFFSL, 0x2d},
+ {0x0000, 0x00} /* EOT */
+};
+
+static const struct st_register def0367dd_qam[] = {
+ {R367CAB_CTRL_1, 0x06}, /* Orginal 0x04 */
+ {R367CAB_CTRL_2, 0x03},
+ {R367CAB_IT_STATUS1, 0x2b},
+ {R367CAB_IT_STATUS2, 0x08},
+ {R367CAB_IT_EN1, 0x00},
+ {R367CAB_IT_EN2, 0x00},
+ {R367CAB_CTRL_STATUS, 0x04},
+ {R367CAB_TEST_CTL, 0x00},
+ {R367CAB_AGC_CTL, 0x73},
+ {R367CAB_AGC_IF_CFG, 0x50},
+ {R367CAB_AGC_RF_CFG, 0x02}, /* RF Freeze */
+ {R367CAB_AGC_PWM_CFG, 0x03},
+ {R367CAB_AGC_PWR_REF_L, 0x5a},
+ {R367CAB_AGC_PWR_REF_H, 0x00},
+ {R367CAB_AGC_RF_TH_L, 0xff},
+ {R367CAB_AGC_RF_TH_H, 0x07},
+ {R367CAB_AGC_IF_LTH_L, 0x00},
+ {R367CAB_AGC_IF_LTH_H, 0x08},
+ {R367CAB_AGC_IF_HTH_L, 0xff},
+ {R367CAB_AGC_IF_HTH_H, 0x07},
+ {R367CAB_AGC_PWR_RD_L, 0xa0},
+ {R367CAB_AGC_PWR_RD_M, 0xe9},
+ {R367CAB_AGC_PWR_RD_H, 0x03},
+ {R367CAB_AGC_PWM_IFCMD_L, 0xe4},
+ {R367CAB_AGC_PWM_IFCMD_H, 0x00},
+ {R367CAB_AGC_PWM_RFCMD_L, 0xff},
+ {R367CAB_AGC_PWM_RFCMD_H, 0x07},
+ {R367CAB_IQDEM_CFG, 0x01},
+ {R367CAB_MIX_NCO_LL, 0x22},
+ {R367CAB_MIX_NCO_HL, 0x96},
+ {R367CAB_MIX_NCO_HH, 0x55},
+ {R367CAB_SRC_NCO_LL, 0xff},
+ {R367CAB_SRC_NCO_LH, 0x0c},
+ {R367CAB_SRC_NCO_HL, 0xf5},
+ {R367CAB_SRC_NCO_HH, 0x20},
+ {R367CAB_IQDEM_GAIN_SRC_L, 0x06},
+ {R367CAB_IQDEM_GAIN_SRC_H, 0x01},
+ {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe},
+ {R367CAB_IQDEM_DCRM_CFG_LH, 0xff},
+ {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f},
+ {R367CAB_IQDEM_DCRM_CFG_HH, 0x00},
+ {R367CAB_IQDEM_ADJ_COEFF0, 0x34},
+ {R367CAB_IQDEM_ADJ_COEFF1, 0xae},
+ {R367CAB_IQDEM_ADJ_COEFF2, 0x46},
+ {R367CAB_IQDEM_ADJ_COEFF3, 0x77},
+ {R367CAB_IQDEM_ADJ_COEFF4, 0x96},
+ {R367CAB_IQDEM_ADJ_COEFF5, 0x69},
+ {R367CAB_IQDEM_ADJ_COEFF6, 0xc7},
+ {R367CAB_IQDEM_ADJ_COEFF7, 0x01},
+ {R367CAB_IQDEM_ADJ_EN, 0x04},
+ {R367CAB_IQDEM_ADJ_AGC_REF, 0x94},
+ {R367CAB_ALLPASSFILT1, 0xc9},
+ {R367CAB_ALLPASSFILT2, 0x2d},
+ {R367CAB_ALLPASSFILT3, 0xa3},
+ {R367CAB_ALLPASSFILT4, 0xfb},
+ {R367CAB_ALLPASSFILT5, 0xf6},
+ {R367CAB_ALLPASSFILT6, 0x45},
+ {R367CAB_ALLPASSFILT7, 0x6f},
+ {R367CAB_ALLPASSFILT8, 0x7e},
+ {R367CAB_ALLPASSFILT9, 0x05},
+ {R367CAB_ALLPASSFILT10, 0x0a},
+ {R367CAB_ALLPASSFILT11, 0x51},
+ {R367CAB_TRL_AGC_CFG, 0x20},
+ {R367CAB_TRL_LPF_CFG, 0x28},
+ {R367CAB_TRL_LPF_ACQ_GAIN, 0x44},
+ {R367CAB_TRL_LPF_TRK_GAIN, 0x22},
+ {R367CAB_TRL_LPF_OUT_GAIN, 0x03},
+ {R367CAB_TRL_LOCKDET_LTH, 0x04},
+ {R367CAB_TRL_LOCKDET_HTH, 0x11},
+ {R367CAB_TRL_LOCKDET_TRGVAL, 0x20},
+ {R367CAB_IQ_QAM, 0x01},
+ {R367CAB_FSM_STATE, 0xa0},
+ {R367CAB_FSM_CTL, 0x08},
+ {R367CAB_FSM_STS, 0x0c},
+ {R367CAB_FSM_SNR0_HTH, 0x00},
+ {R367CAB_FSM_SNR1_HTH, 0x00},
+ {R367CAB_FSM_SNR2_HTH, 0x00},
+ {R367CAB_FSM_SNR0_LTH, 0x00},
+ {R367CAB_FSM_SNR1_LTH, 0x00},
+ {R367CAB_FSM_EQA1_HTH, 0x00},
+ {R367CAB_FSM_TEMPO, 0x32},
+ {R367CAB_FSM_CONFIG, 0x03},
+ {R367CAB_EQU_I_TESTTAP_L, 0x11},
+ {R367CAB_EQU_I_TESTTAP_M, 0x00},
+ {R367CAB_EQU_I_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TESTAP_CFG, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_L, 0xff},
+ {R367CAB_EQU_Q_TESTTAP_M, 0x00},
+ {R367CAB_EQU_Q_TESTTAP_H, 0x00},
+ {R367CAB_EQU_TAP_CTRL, 0x00},
+ {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11},
+ {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05},
+ {R367CAB_EQU_CTR_HIPOW_L, 0x00},
+ {R367CAB_EQU_CTR_HIPOW_H, 0x00},
+ {R367CAB_EQU_I_EQU_LO, 0xef},
+ {R367CAB_EQU_I_EQU_HI, 0x00},
+ {R367CAB_EQU_Q_EQU_LO, 0xee},
+ {R367CAB_EQU_Q_EQU_HI, 0x00},
+ {R367CAB_EQU_MAPPER, 0xc5},
+ {R367CAB_EQU_SWEEP_RATE, 0x80},
+ {R367CAB_EQU_SNR_LO, 0x64},
+ {R367CAB_EQU_SNR_HI, 0x03},
+ {R367CAB_EQU_GAMMA_LO, 0x00},
+ {R367CAB_EQU_GAMMA_HI, 0x00},
+ {R367CAB_EQU_ERR_GAIN, 0x36},
+ {R367CAB_EQU_RADIUS, 0xaa},
+ {R367CAB_EQU_FFE_MAINTAP, 0x00},
+ {R367CAB_EQU_FFE_LEAKAGE, 0x63},
+ {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf},
+ {R367CAB_EQU_GAIN_WIDE, 0x88},
+ {R367CAB_EQU_GAIN_NARROW, 0x41},
+ {R367CAB_EQU_CTR_LPF_GAIN, 0xd1},
+ {R367CAB_EQU_CRL_LPF_GAIN, 0xa7},
+ {R367CAB_EQU_GLOBAL_GAIN, 0x06},
+ {R367CAB_EQU_CRL_LD_SEN, 0x85},
+ {R367CAB_EQU_CRL_LD_VAL, 0xe2},
+ {R367CAB_EQU_CRL_TFR, 0x20},
+ {R367CAB_EQU_CRL_BISTH_LO, 0x00},
+ {R367CAB_EQU_CRL_BISTH_HI, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_LO, 0x00},
+ {R367CAB_EQU_SWEEP_RANGE_HI, 0x00},
+ {R367CAB_EQU_CRL_LIMITER, 0x40},
+ {R367CAB_EQU_MODULUS_MAP, 0x90},
+ {R367CAB_EQU_PNT_GAIN, 0xa7},
+ {R367CAB_FEC_AC_CTR_0, 0x16},
+ {R367CAB_FEC_AC_CTR_1, 0x0b},
+ {R367CAB_FEC_AC_CTR_2, 0x88},
+ {R367CAB_FEC_AC_CTR_3, 0x02},
+ {R367CAB_FEC_STATUS, 0x12},
+ {R367CAB_RS_COUNTER_0, 0x7d},
+ {R367CAB_RS_COUNTER_1, 0xd0},
+ {R367CAB_RS_COUNTER_2, 0x19},
+ {R367CAB_RS_COUNTER_3, 0x0b},
+ {R367CAB_RS_COUNTER_4, 0xa3},
+ {R367CAB_RS_COUNTER_5, 0x00},
+ {R367CAB_BERT_0, 0x01},
+ {R367CAB_BERT_1, 0x25},
+ {R367CAB_BERT_2, 0x41},
+ {R367CAB_BERT_3, 0x39},
+ {R367CAB_OUTFORMAT_0, 0xc2},
+ {R367CAB_OUTFORMAT_1, 0x22},
+ {R367CAB_SMOOTHER_2, 0x28},
+ {R367CAB_TSMF_CTRL_0, 0x01},
+ {R367CAB_TSMF_CTRL_1, 0xc6},
+ {R367CAB_TSMF_CTRL_3, 0x43},
+ {R367CAB_TS_ON_ID_0, 0x00},
+ {R367CAB_TS_ON_ID_1, 0x00},
+ {R367CAB_TS_ON_ID_2, 0x00},
+ {R367CAB_TS_ON_ID_3, 0x00},
+ {R367CAB_RE_STATUS_0, 0x00},
+ {R367CAB_RE_STATUS_1, 0x00},
+ {R367CAB_RE_STATUS_2, 0x00},
+ {R367CAB_RE_STATUS_3, 0x00},
+ {R367CAB_TS_STATUS_0, 0x00},
+ {R367CAB_TS_STATUS_1, 0x00},
+ {R367CAB_TS_STATUS_2, 0xa0},
+ {R367CAB_TS_STATUS_3, 0x00},
+ {R367CAB_T_O_ID_0, 0x00},
+ {R367CAB_T_O_ID_1, 0x00},
+ {R367CAB_T_O_ID_2, 0x00},
+ {R367CAB_T_O_ID_3, 0x00},
+ {0x0000, 0x00} /* EOT */
+};
+
+static const struct st_register def0367dd_base[] = {
+ {R367TER_IOCFG0, 0x80},
+ {R367TER_DAC0R, 0x00},
+ {R367TER_IOCFG1, 0x00},
+ {R367TER_DAC1R, 0x00},
+ {R367TER_IOCFG2, 0x00},
+ {R367TER_SDFR, 0x00},
+ {R367TER_AUX_CLK, 0x00},
+ {R367TER_FREESYS1, 0x00},
+ {R367TER_FREESYS2, 0x00},
+ {R367TER_FREESYS3, 0x00},
+ {R367TER_GPIO_CFG, 0x55},
+ {R367TER_GPIO_CMD, 0x01},
+ {R367TER_TSTRES, 0x00},
+ {R367TER_ANACTRL, 0x00},
+ {R367TER_TSTBUS, 0x00},
+ {R367TER_RF_AGC2, 0x20},
+ {R367TER_ANADIGCTRL, 0x0b},
+ {R367TER_PLLMDIV, 0x01},
+ {R367TER_PLLNDIV, 0x08},
+ {R367TER_PLLSETUP, 0x18},
+ {R367TER_DUAL_AD12, 0x04},
+ {R367TER_TSTBIST, 0x00},
+ {0x0000, 0x00} /* EOT */
+};
+
+/*
+ * Tables combined
+ */
+
+static const struct
+st_register *stv0367_deftabs[STV0367_DEFTAB_MAX][STV0367_TAB_MAX] = {
+ /* generic default/init tabs */
+ { def0367ter, def0367cab, NULL },
+ /* default tabs for digital devices cards/flex modules */
+ { def0367dd_ofdm, def0367dd_qam, def0367dd_base },
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/stv0367_regs.h b/drivers/media/dvb-frontends/stv0367_regs.h
index 1d1586221239..cc66d93c5a1f 100644
--- a/drivers/media/dvb-frontends/stv0367_regs.h
+++ b/drivers/media/dvb-frontends/stv0367_regs.h
@@ -2639,8 +2639,6 @@
#define R367TER_DEBUG_LT9 0xf405
#define F367TER_F_DEBUG_LT9 0xf40500ff
-#define STV0367TER_NBREGS 445
-
/* ID */
#define R367CAB_ID 0xf000
#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff
@@ -3605,6 +3603,4 @@
#define R367CAB_T_O_ID_3 0xf4d3
#define F367CAB_TS_ID_I_H 0xf4d300ff
-#define STV0367CAB_NBREGS 187
-
#endif
diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c
index 47c0549eb7b2..1c689f7f4ab8 100644
--- a/drivers/media/dvb-frontends/zl10353.c
+++ b/drivers/media/dvb-frontends/zl10353.c
@@ -211,7 +211,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe)
break;
default:
c->bandwidth_hz = 8000000;
- /* fall though */
+ /* fall through */
case 8000000:
zl10353_single_write(fe, MCLK_RATIO, 0x75);
zl10353_single_write(fe, 0x64, 0x36);
@@ -268,6 +268,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe)
if (c->hierarchy == HIERARCHY_AUTO ||
c->hierarchy == HIERARCHY_NONE)
break;
+ /* fall through */
default:
return -EINVAL;
}
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index aaa9471c7d11..121b3b5394cb 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -209,6 +209,7 @@ config VIDEO_ADV7604
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
depends on GPIOLIB || COMPILE_TEST
select HDMI
+ select V4L2_FWNODE
---help---
Support for the Analog Devices ADV7604 video decoder.
@@ -302,6 +303,16 @@ config VIDEO_AD5820
This is a driver for the AD5820 camera lens voice coil.
It is used for example in Nokia N900 (RX-51).
+config VIDEO_DW9714
+ tristate "DW9714 lens voice coil support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a driver for the DW9714 camera lens voice coil.
+ DW9714 is a 10 bit DAC with 120mA output current sink
+ capability. This is designed for linear control of
+ voice coil motors, controlled via I2C serial interface.
+
config VIDEO_SAA7110
tristate "Philips SAA7110 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -324,6 +335,7 @@ config VIDEO_TC358743
tristate "Toshiba TC358743 decoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
select HDMI
+ select V4L2_FWNODE
---help---
Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge.
@@ -333,6 +345,7 @@ config VIDEO_TC358743
config VIDEO_TVP514X
tristate "Texas Instruments TVP514x video decoder"
depends on VIDEO_V4L2 && I2C
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
decoder. It is currently working with the TI OMAP3 camera
@@ -344,6 +357,7 @@ config VIDEO_TVP514X
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
+ select V4L2_FWNODE
---help---
Support for the Texas Instruments TVP5150 video decoder.
@@ -353,6 +367,7 @@ config VIDEO_TVP5150
config VIDEO_TVP7002
tristate "Texas Instruments TVP7002 video decoder"
depends on VIDEO_V4L2 && I2C
+ select V4L2_FWNODE
---help---
Support for the Texas Instruments TVP7002 video decoder.
@@ -535,6 +550,7 @@ config VIDEO_OV2659
tristate "OmniVision OV2659 sensor support"
depends on VIDEO_V4L2 && I2C
depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV2659 camera.
@@ -542,11 +558,22 @@ config VIDEO_OV2659
To compile this driver as a module, choose M here: the
module will be called ov2659.
+config VIDEO_OV5640
+ tristate "OmniVision OV5640 sensor support"
+ depends on OF
+ depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Omnivision
+ OV5640 camera sensor with a MIPI CSI-2 interface.
+
config VIDEO_OV5645
tristate "OmniVision OV5645 sensor support"
depends on OF
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV5645 camera.
@@ -558,6 +585,7 @@ config VIDEO_OV5647
tristate "OmniVision OV5647 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV5647 camera.
@@ -592,6 +620,14 @@ config VIDEO_OV9650
This is a V4L2 sensor-level driver for the Omnivision
OV9650 and OV9652 camera sensors.
+config VIDEO_OV13858
+ tristate "OmniVision OV13858 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV13858 camera.
+
config VIDEO_VS6624
tristate "ST VS6624 sensor support"
depends on VIDEO_V4L2 && I2C
@@ -650,6 +686,7 @@ config VIDEO_MT9V032
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
select REGMAP_I2C
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the Micron
MT9V032 752x480 CMOS sensor.
@@ -697,6 +734,7 @@ config VIDEO_S5K4ECGX
config VIDEO_S5K5BAF
tristate "Samsung S5K5BAF sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
---help---
This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M
camera sensor with an embedded SoC image signal processor.
@@ -707,6 +745,7 @@ source "drivers/media/i2c/et8ek8/Kconfig"
config VIDEO_S5C73M3
tristate "Samsung S5C73M3 sensor support"
depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
---help---
This is a V4L2 sensor-level driver for Samsung S5C73M3
8 Mpixel camera.
@@ -785,6 +824,18 @@ config VIDEO_SAA6752HS
To compile this driver as a module, choose M here: the
module will be called saa6752hs.
+comment "SDR tuner chips"
+
+config SDR_MAX2175
+ tristate "Maxim 2175 RF to Bits tuner"
+ depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C
+ ---help---
+ Support for Maxim 2175 tuner. It is an advanced analog/digital
+ radio receiver with RF-to-Bits front-end designed for SDR solutions.
+
+ To compile this driver as a module, choose M here; the
+ module will be called max2175.
+
comment "Miscellaneous helper chips"
config VIDEO_THS7303
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 62323ec66be8..2c0868fa6034 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
obj-$(CONFIG_VIDEO_AD5820) += ad5820.o
+obj-$(CONFIG_VIDEO_DW9714) += dw9714.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
@@ -58,11 +59,13 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
+obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
@@ -86,3 +89,5 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
+
+obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c
index 3d2a3c6b67d8..034ebf754007 100644
--- a/drivers/media/i2c/ad5820.c
+++ b/drivers/media/i2c/ad5820.c
@@ -341,7 +341,7 @@ static int ad5820_remove(struct i2c_client *client)
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct ad5820_device *coil = to_ad5820_device(subdev);
- v4l2_device_unregister_subdev(&coil->subdev);
+ v4l2_async_unregister_subdev(&coil->subdev);
v4l2_ctrl_handler_free(&coil->ctrls);
media_entity_cleanup(&coil->subdev.entity);
mutex_destroy(&coil->power_lock);
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index bdbbf8cf27e4..78de7ddf5081 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1452,6 +1452,8 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
#ifdef CONFIG_OF
static const struct of_device_id adv7180_of_id[] = {
{ .compatible = "adi,adv7180", },
+ { .compatible = "adi,adv7180cp", },
+ { .compatible = "adi,adv7180st", },
{ .compatible = "adi,adv7182", },
{ .compatible = "adi,adv7280", },
{ .compatible = "adi,adv7280-m", },
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index f1fa9cec489f..660bacb8f7d9 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -33,6 +33,7 @@
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/slab.h>
#include <linux/v4l2-dv-timings.h>
#include <linux/videodev2.h>
@@ -45,7 +46,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
static int debug;
module_param(debug, int, 0644);
@@ -3069,7 +3070,7 @@ MODULE_DEVICE_TABLE(of, adv76xx_of_id);
static int adv76xx_parse_dt(struct adv76xx_state *state)
{
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct device_node *endpoint;
struct device_node *np;
unsigned int flags;
@@ -3083,7 +3084,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
if (!endpoint)
return -EINVAL;
- ret = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg);
if (ret) {
of_node_put(endpoint);
return ret;
diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c
index b6aeceea9850..af5db71a0888 100644
--- a/drivers/media/i2c/as3645a.c
+++ b/drivers/media/i2c/as3645a.c
@@ -294,8 +294,8 @@ static int as3645a_read_fault(struct as3645a *flash)
dev_dbg(&client->dev, "Inductor Peak limit fault\n");
if (rval & AS_FAULT_INFO_INDICATOR_LED)
- dev_dbg(&client->dev, "Indicator LED fault: "
- "Short circuit or open loop\n");
+ dev_dbg(&client->dev,
+ "Indicator LED fault: Short circuit or open loop\n");
dev_dbg(&client->dev, "%u connected LEDs\n",
rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1);
@@ -310,8 +310,8 @@ static int as3645a_read_fault(struct as3645a *flash)
dev_dbg(&client->dev, "Short circuit fault\n");
if (rval & AS_FAULT_INFO_OVER_VOLTAGE)
- dev_dbg(&client->dev, "Over voltage fault: "
- "Indicates missing capacitor or open connection\n");
+ dev_dbg(&client->dev,
+ "Over voltage fault: Indicates missing capacitor or open connection\n");
return rval;
}
@@ -583,8 +583,8 @@ static int as3645a_registered(struct v4l2_subdev *sd)
/* Verify the chip model and version. */
if (model != 0x01 || rfu != 0x00) {
- dev_err(&client->dev, "AS3645A not detected "
- "(model %d rfu %d)\n", model, rfu);
+ dev_err(&client->dev,
+ "AS3645A not detected (model %d rfu %d)\n", model, rfu);
rval = -ENODEV;
goto power_off;
}
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index b8d3c070bfc1..39f51daa7558 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -416,11 +416,13 @@ static void cx25840_initialize(struct i2c_client *client)
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
/* 6. */
cx25840_write(client, 0x115, 0x8c);
@@ -630,11 +632,13 @@ static void cx23885_initialize(struct i2c_client *client)
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
/* Call the cx23888 specific std setup func, we no longer rely on
* the generic cx24840 func.
@@ -748,11 +752,13 @@ static void cx231xx_initialize(struct i2c_client *client)
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
cx25840_std_setup(client);
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
new file mode 100644
index 000000000000..6a607d7f82de
--- /dev/null
+++ b/drivers/media/i2c/dw9714.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2015--2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define DW9714_NAME "dw9714"
+#define DW9714_MAX_FOCUS_POS 1023
+/*
+ * This acts as the minimum granularity of lens movement.
+ * Keep this value power of 2, so the control steps can be
+ * uniformly adjusted for gradual lens movement, with desired
+ * number of control steps.
+ */
+#define DW9714_CTRL_STEPS 16
+#define DW9714_CTRL_DELAY_US 1000
+/*
+ * S[3:2] = 0x00, codes per step for "Linear Slope Control"
+ * S[1:0] = 0x00, step period
+ */
+#define DW9714_DEFAULT_S 0x0
+#define DW9714_VAL(data, s) ((data) << 4 | (s))
+
+/* dw9714 device structure */
+struct dw9714_device {
+ struct i2c_client *client;
+ struct v4l2_ctrl_handler ctrls_vcm;
+ struct v4l2_subdev sd;
+ u16 current_val;
+};
+
+static inline struct dw9714_device *to_dw9714_vcm(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct dw9714_device, ctrls_vcm);
+}
+
+static inline struct dw9714_device *sd_to_dw9714_vcm(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct dw9714_device, sd);
+}
+
+static int dw9714_i2c_write(struct i2c_client *client, u16 data)
+{
+ int ret;
+ u16 val = cpu_to_be16(data);
+
+ ret = i2c_master_send(client, (const char *)&val, sizeof(val));
+ if (ret != sizeof(val)) {
+ dev_err(&client->dev, "I2C write fail\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int dw9714_t_focus_vcm(struct dw9714_device *dw9714_dev, u16 val)
+{
+ struct i2c_client *client = dw9714_dev->client;
+
+ dw9714_dev->current_val = val;
+
+ return dw9714_i2c_write(client, DW9714_VAL(val, DW9714_DEFAULT_S));
+}
+
+static int dw9714_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct dw9714_device *dev_vcm = to_dw9714_vcm(ctrl);
+
+ if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
+ return dw9714_t_focus_vcm(dev_vcm, ctrl->val);
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops dw9714_vcm_ctrl_ops = {
+ .s_ctrl = dw9714_set_ctrl,
+};
+
+static int dw9714_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd);
+ struct device *dev = &dw9714_dev->client->dev;
+ int rval;
+
+ rval = pm_runtime_get_sync(dev);
+ if (rval < 0) {
+ pm_runtime_put_noidle(dev);
+ return rval;
+ }
+
+ return 0;
+}
+
+static int dw9714_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd);
+ struct device *dev = &dw9714_dev->client->dev;
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops dw9714_int_ops = {
+ .open = dw9714_open,
+ .close = dw9714_close,
+};
+
+static const struct v4l2_subdev_ops dw9714_ops = { };
+
+static void dw9714_subdev_cleanup(struct dw9714_device *dw9714_dev)
+{
+ v4l2_async_unregister_subdev(&dw9714_dev->sd);
+ v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm);
+ media_entity_cleanup(&dw9714_dev->sd.entity);
+}
+
+static int dw9714_init_controls(struct dw9714_device *dev_vcm)
+{
+ struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm;
+ const struct v4l2_ctrl_ops *ops = &dw9714_vcm_ctrl_ops;
+ struct i2c_client *client = dev_vcm->client;
+
+ v4l2_ctrl_handler_init(hdl, 1);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
+ 0, DW9714_MAX_FOCUS_POS, DW9714_CTRL_STEPS, 0);
+
+ if (hdl->error)
+ dev_err(&client->dev, "%s fail error: 0x%x\n",
+ __func__, hdl->error);
+ dev_vcm->sd.ctrl_handler = hdl;
+ return hdl->error;
+}
+
+static int dw9714_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct dw9714_device *dw9714_dev;
+ int rval;
+
+ dw9714_dev = devm_kzalloc(&client->dev, sizeof(*dw9714_dev),
+ GFP_KERNEL);
+ if (dw9714_dev == NULL)
+ return -ENOMEM;
+
+ dw9714_dev->client = client;
+
+ v4l2_i2c_subdev_init(&dw9714_dev->sd, client, &dw9714_ops);
+ dw9714_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ dw9714_dev->sd.internal_ops = &dw9714_int_ops;
+
+ rval = dw9714_init_controls(dw9714_dev);
+ if (rval)
+ goto err_cleanup;
+
+ rval = media_entity_pads_init(&dw9714_dev->sd.entity, 0, NULL);
+ if (rval < 0)
+ goto err_cleanup;
+
+ dw9714_dev->sd.entity.function = MEDIA_ENT_F_LENS;
+
+ rval = v4l2_async_register_subdev(&dw9714_dev->sd);
+ if (rval < 0)
+ goto err_cleanup;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ return 0;
+
+err_cleanup:
+ dw9714_subdev_cleanup(dw9714_dev);
+ dev_err(&client->dev, "Probe failed: %d\n", rval);
+ return rval;
+}
+
+static int dw9714_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd);
+
+ pm_runtime_disable(&client->dev);
+ dw9714_subdev_cleanup(dw9714_dev);
+
+ return 0;
+}
+
+/*
+ * This function sets the vcm position, so it consumes least current
+ * The lens position is gradually moved in units of DW9714_CTRL_STEPS,
+ * to make the movements smoothly.
+ */
+static int __maybe_unused dw9714_vcm_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd);
+ int ret, val;
+
+ for (val = dw9714_dev->current_val & ~(DW9714_CTRL_STEPS - 1);
+ val >= 0; val -= DW9714_CTRL_STEPS) {
+ ret = dw9714_i2c_write(client,
+ DW9714_VAL(val, DW9714_DEFAULT_S));
+ if (ret)
+ dev_err_once(dev, "%s I2C failure: %d", __func__, ret);
+ usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
+ }
+ return 0;
+}
+
+/*
+ * This function sets the vcm position to the value set by the user
+ * through v4l2_ctrl_ops s_ctrl handler
+ * The lens position is gradually moved in units of DW9714_CTRL_STEPS,
+ * to make the movements smoothly.
+ */
+static int __maybe_unused dw9714_vcm_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd);
+ int ret, val;
+
+ for (val = dw9714_dev->current_val % DW9714_CTRL_STEPS;
+ val < dw9714_dev->current_val + DW9714_CTRL_STEPS - 1;
+ val += DW9714_CTRL_STEPS) {
+ ret = dw9714_i2c_write(client,
+ DW9714_VAL(val, DW9714_DEFAULT_S));
+ if (ret)
+ dev_err_ratelimited(dev, "%s I2C failure: %d",
+ __func__, ret);
+ usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id dw9714_acpi_match[] = {
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, dw9714_acpi_match);
+#endif
+
+static const struct i2c_device_id dw9714_id_table[] = {
+ {DW9714_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, dw9714_id_table);
+
+static const struct dev_pm_ops dw9714_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dw9714_vcm_suspend, dw9714_vcm_resume)
+ SET_RUNTIME_PM_OPS(dw9714_vcm_suspend, dw9714_vcm_resume, NULL)
+};
+
+static struct i2c_driver dw9714_i2c_driver = {
+ .driver = {
+ .name = DW9714_NAME,
+ .pm = &dw9714_pm_ops,
+ .acpi_match_table = ACPI_PTR(dw9714_acpi_match),
+ },
+ .probe = dw9714_probe,
+ .remove = dw9714_remove,
+ .id_table = dw9714_id_table,
+};
+
+module_i2c_driver(dw9714_i2c_driver);
+
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Jian Xu Zheng <jian.xu.zheng@intel.com>");
+MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
+MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>");
+MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>");
+MODULE_DESCRIPTION("DW9714 VCM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index 6e313d5243a0..f39f5179dd95 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -1496,7 +1496,6 @@ MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
static const struct dev_pm_ops et8ek8_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(et8ek8_suspend, et8ek8_resume)
};
-MODULE_DEVICE_TABLE(of, et8ek8_of_table);
static struct i2c_driver et8ek8_i2c_driver = {
.driver = {
diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c
new file mode 100644
index 000000000000..a4736a8a7792
--- /dev/null
+++ b/drivers/media/i2c/max2175.c
@@ -0,0 +1,1453 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this device.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2017 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/max2175.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "max2175.h"
+
+#define DRIVER_NAME "max2175"
+
+#define mxm_dbg(ctx, fmt, arg...) dev_dbg(&ctx->client->dev, fmt, ## arg)
+#define mxm_err(ctx, fmt, arg...) dev_err(&ctx->client->dev, fmt, ## arg)
+
+/* Rx mode */
+struct max2175_rxmode {
+ enum max2175_band band; /* Associated band */
+ u32 freq; /* Default freq in Hz */
+ u8 i2s_word_size; /* Bit value */
+};
+
+/* Register map to define preset values */
+struct max2175_reg_map {
+ u8 idx; /* Register index */
+ u8 val; /* Register value */
+};
+
+static const struct max2175_rxmode eu_rx_modes[] = {
+ /* EU modes */
+ [MAX2175_EU_FM_1_2] = { MAX2175_BAND_FM, 98256000, 1 },
+ [MAX2175_DAB_1_2] = { MAX2175_BAND_VHF, 182640000, 0 },
+};
+
+static const struct max2175_rxmode na_rx_modes[] = {
+ /* NA modes */
+ [MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1 },
+ [MAX2175_NA_FM_2_0] = { MAX2175_BAND_FM, 98255520, 6 },
+};
+
+/*
+ * Preset values:
+ * Based on Maxim MAX2175 Register Table revision: 130p10
+ */
+static const u8 full_fm_eu_1p0[] = {
+ 0x15, 0x04, 0xb8, 0xe3, 0x35, 0x18, 0x7c, 0x00,
+ 0x00, 0x7d, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+ 0x61, 0x61, 0x61, 0x61, 0x5a, 0x0f, 0x34, 0x1c,
+ 0x14, 0x88, 0x33, 0x02, 0x00, 0x09, 0x00, 0x65,
+ 0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0x2f, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+ 0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+ 0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+ 0x00, 0x47, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+ 0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+ 0x1b,
+};
+
+static const u8 full_fm_na_1p0[] = {
+ 0x13, 0x08, 0x8d, 0xc0, 0x35, 0x18, 0x7d, 0x3f,
+ 0x7d, 0x75, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+ 0x61, 0x61, 0x61, 0x61, 0x5c, 0x0f, 0x34, 0x1c,
+ 0x14, 0x88, 0x33, 0x02, 0x00, 0x01, 0x00, 0x65,
+ 0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0xaf, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+ 0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+ 0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+ 0x00, 0x35, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+ 0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+ 0x1b,
+};
+
+/* DAB1.2 settings */
+static const struct max2175_reg_map dab12_map[] = {
+ { 0x01, 0x13 }, { 0x02, 0x0d }, { 0x03, 0x15 }, { 0x04, 0x55 },
+ { 0x05, 0x0a }, { 0x06, 0xa0 }, { 0x07, 0x40 }, { 0x08, 0x00 },
+ { 0x09, 0x00 }, { 0x0a, 0x7d }, { 0x0b, 0x4a }, { 0x0c, 0x28 },
+ { 0x0e, 0x43 }, { 0x0f, 0xb5 }, { 0x10, 0x31 }, { 0x11, 0x9e },
+ { 0x12, 0x68 }, { 0x13, 0x9e }, { 0x14, 0x68 }, { 0x15, 0x58 },
+ { 0x16, 0x2f }, { 0x17, 0x3f }, { 0x18, 0x40 }, { 0x1a, 0x88 },
+ { 0x1b, 0xaa }, { 0x1c, 0x9a }, { 0x1d, 0x00 }, { 0x1e, 0x00 },
+ { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 },
+ { 0x27, 0x00 }, { 0x32, 0x08 }, { 0x33, 0xf8 }, { 0x36, 0x2d },
+ { 0x37, 0x7e }, { 0x55, 0xaf }, { 0x56, 0x3f }, { 0x57, 0xf8 },
+ { 0x58, 0x99 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x02 },
+ { 0x79, 0x40 }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x85, 0x00 },
+ { 0x86, 0x20 },
+};
+
+/* EU FM 1.2 settings */
+static const struct max2175_reg_map fmeu1p2_map[] = {
+ { 0x01, 0x15 }, { 0x02, 0x04 }, { 0x03, 0xb8 }, { 0x04, 0xe3 },
+ { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x00 },
+ { 0x09, 0x00 }, { 0x0a, 0x73 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+ { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+ { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5a },
+ { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+ { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+ { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+ { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0x2f },
+ { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+ { 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0x40 }, { 0x78, 0x00 },
+ { 0x79, 0x00 }, { 0x82, 0x47 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+ { 0x86, 0x3f },
+};
+
+/* FM NA 1.0 settings */
+static const struct max2175_reg_map fmna1p0_map[] = {
+ { 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+ { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7d }, { 0x08, 0x3f },
+ { 0x09, 0x7d }, { 0x0a, 0x75 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+ { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+ { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+ { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+ { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+ { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+ { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+ { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+ { 0x58, 0x9f }, { 0x76, 0xa6 }, { 0x77, 0x40 }, { 0x78, 0x00 },
+ { 0x79, 0x00 }, { 0x82, 0x35 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+ { 0x86, 0x3f },
+};
+
+/* FM NA 2.0 settings */
+static const struct max2175_reg_map fmna2p0_map[] = {
+ { 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+ { 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x54 },
+ { 0x09, 0xa7 }, { 0x0a, 0x55 }, { 0x0b, 0x42 }, { 0x0c, 0x48 },
+ { 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+ { 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+ { 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+ { 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+ { 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+ { 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+ { 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+ { 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0xc0 }, { 0x78, 0x00 },
+ { 0x79, 0x00 }, { 0x82, 0x6b }, { 0x83, 0x00 }, { 0x85, 0x11 },
+ { 0x86, 0x3f },
+};
+
+static const u16 ch_coeff_dab1[] = {
+ 0x001c, 0x0007, 0xffcd, 0x0056, 0xffa4, 0x0033, 0x0027, 0xff61,
+ 0x010e, 0xfec0, 0x0106, 0xffb8, 0xff1c, 0x023c, 0xfcb2, 0x039b,
+ 0xfd4e, 0x0055, 0x036a, 0xf7de, 0x0d21, 0xee72, 0x1499, 0x6a51,
+};
+
+static const u16 ch_coeff_fmeu[] = {
+ 0x0000, 0xffff, 0x0001, 0x0002, 0xfffa, 0xffff, 0x0015, 0xffec,
+ 0xffde, 0x0054, 0xfff9, 0xff52, 0x00b8, 0x00a2, 0xfe0a, 0x00af,
+ 0x02e3, 0xfc14, 0xfe89, 0x089d, 0xfa2e, 0xf30f, 0x25be, 0x4eb6,
+};
+
+static const u16 eq_coeff_fmeu1_ra02_m6db[] = {
+ 0x0040, 0xffc6, 0xfffa, 0x002c, 0x000d, 0xff90, 0x0037, 0x006e,
+ 0xffc0, 0xff5b, 0x006a, 0x00f0, 0xff57, 0xfe94, 0x0112, 0x0252,
+ 0xfe0c, 0xfc6a, 0x0385, 0x0553, 0xfa49, 0xf789, 0x0b91, 0x1a10,
+};
+
+static const u16 ch_coeff_fmna[] = {
+ 0x0001, 0x0003, 0xfffe, 0xfff4, 0x0000, 0x001f, 0x000c, 0xffbc,
+ 0xffd3, 0x007d, 0x0075, 0xff33, 0xff01, 0x0131, 0x01ef, 0xfe60,
+ 0xfc7a, 0x020e, 0x0656, 0xfd94, 0xf395, 0x02ab, 0x2857, 0x3d3f,
+};
+
+static const u16 eq_coeff_fmna1_ra02_m6db[] = {
+ 0xfff1, 0xffe1, 0xffef, 0x000e, 0x0030, 0x002f, 0xfff6, 0xffa7,
+ 0xff9d, 0x000a, 0x00a2, 0x00b5, 0xffea, 0xfed9, 0xfec5, 0x003d,
+ 0x0217, 0x021b, 0xff5a, 0xfc2b, 0xfcbd, 0x02c4, 0x0ac3, 0x0e85,
+};
+
+static const u8 adc_presets[2][23] = {
+ {
+ 0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49,
+ 0x00, 0x00, 0x00, 0x8c, 0x02, 0x02, 0x00, 0x04,
+ 0xec, 0x82, 0x4b, 0xcc, 0x01, 0x88, 0x0c,
+ },
+ {
+ 0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49,
+ 0x00, 0x00, 0x00, 0x8c, 0x02, 0x20, 0x33, 0x8c,
+ 0x57, 0xd7, 0x59, 0xb7, 0x65, 0x0e, 0x0c,
+ },
+};
+
+/* Tuner bands */
+static const struct v4l2_frequency_band eu_bands_rf = {
+ .tuner = 0,
+ .type = V4L2_TUNER_RF,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 65000000,
+ .rangehigh = 240000000,
+};
+
+static const struct v4l2_frequency_band na_bands_rf = {
+ .tuner = 0,
+ .type = V4L2_TUNER_RF,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 65000000,
+ .rangehigh = 108000000,
+};
+
+/* Regmap settings */
+static const struct regmap_range max2175_regmap_volatile_range[] = {
+ regmap_reg_range(0x30, 0x35),
+ regmap_reg_range(0x3a, 0x45),
+ regmap_reg_range(0x59, 0x5e),
+ regmap_reg_range(0x73, 0x75),
+};
+
+static const struct regmap_access_table max2175_volatile_regs = {
+ .yes_ranges = max2175_regmap_volatile_range,
+ .n_yes_ranges = ARRAY_SIZE(max2175_regmap_volatile_range),
+};
+
+static const struct reg_default max2175_reg_defaults[] = {
+ { 0x00, 0x07},
+};
+
+static const struct regmap_config max2175_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .reg_defaults = max2175_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults),
+ .volatile_table = &max2175_volatile_regs,
+ .cache_type = REGCACHE_FLAT,
+};
+
+struct max2175 {
+ struct v4l2_subdev sd; /* Sub-device */
+ struct i2c_client *client; /* I2C client */
+
+ /* Controls */
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_ctrl *lna_gain; /* LNA gain value */
+ struct v4l2_ctrl *if_gain; /* I/F gain value */
+ struct v4l2_ctrl *pll_lock; /* PLL lock */
+ struct v4l2_ctrl *i2s_en; /* I2S output enable */
+ struct v4l2_ctrl *hsls; /* High-side/Low-side polarity */
+ struct v4l2_ctrl *rx_mode; /* Receive mode */
+
+ /* Regmap */
+ struct regmap *regmap;
+
+ /* Cached configuration */
+ u32 freq; /* Tuned freq In Hz */
+ const struct max2175_rxmode *rx_modes; /* EU or NA modes */
+ const struct v4l2_frequency_band *bands_rf; /* EU or NA bands */
+
+ /* Device settings */
+ unsigned long xtal_freq; /* Ref Oscillator freq in Hz */
+ u32 decim_ratio;
+ bool master; /* Master/Slave */
+ bool am_hiz; /* AM Hi-Z filter */
+
+ /* ROM values */
+ u8 rom_bbf_bw_am;
+ u8 rom_bbf_bw_fm;
+ u8 rom_bbf_bw_dab;
+
+ /* Driver private variables */
+ bool mode_resolved; /* Flag to sanity check settings */
+};
+
+static inline struct max2175 *max2175_from_sd(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct max2175, sd);
+}
+
+static inline struct max2175 *max2175_from_ctrl_hdl(struct v4l2_ctrl_handler *h)
+{
+ return container_of(h, struct max2175, ctrl_hdl);
+}
+
+/* Get bitval of a given val */
+static inline u8 max2175_get_bitval(u8 val, u8 msb, u8 lsb)
+{
+ return (val & GENMASK(msb, lsb)) >> lsb;
+}
+
+/* Read/Write bit(s) on top of regmap */
+static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
+{
+ u32 regval;
+ int ret;
+
+ ret = regmap_read(ctx->regmap, idx, &regval);
+ if (ret)
+ mxm_err(ctx, "read ret(%d): idx 0x%02x\n", ret, idx);
+ else
+ *val = regval;
+
+ return ret;
+}
+
+static int max2175_write(struct max2175 *ctx, u8 idx, u8 val)
+{
+ int ret;
+
+ ret = regmap_write(ctx->regmap, idx, val);
+ if (ret)
+ mxm_err(ctx, "write ret(%d): idx 0x%02x val 0x%02x\n",
+ ret, idx, val);
+
+ return ret;
+}
+
+static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb)
+{
+ u8 val;
+
+ if (max2175_read(ctx, idx, &val))
+ return 0;
+
+ return max2175_get_bitval(val, msb, lsb);
+}
+
+static int max2175_write_bits(struct max2175 *ctx, u8 idx,
+ u8 msb, u8 lsb, u8 newval)
+{
+ int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
+ newval << lsb);
+
+ if (ret)
+ mxm_err(ctx, "wbits ret(%d): idx 0x%02x\n", ret, idx);
+
+ return ret;
+}
+
+static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval)
+{
+ return max2175_write_bits(ctx, idx, bit, bit, newval);
+}
+
+/* Checks expected pattern every msec until timeout */
+static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
+ u8 exp_bitval, u32 timeout_us)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(ctx->regmap, idx, val,
+ (max2175_get_bitval(val, msb, lsb) == exp_bitval),
+ 1000, timeout_us);
+}
+
+static int max2175_poll_csm_ready(struct max2175 *ctx)
+{
+ int ret;
+
+ ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50000);
+ if (ret)
+ mxm_err(ctx, "csm not ready\n");
+
+ return ret;
+}
+
+#define MAX2175_IS_BAND_AM(ctx) \
+ (max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
+
+#define MAX2175_IS_BAND_VHF(ctx) \
+ (max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+
+#define MAX2175_IS_FM_MODE(ctx) \
+ (max2175_read_bits(ctx, 12, 5, 4) == 0)
+
+#define MAX2175_IS_FMHD_MODE(ctx) \
+ (max2175_read_bits(ctx, 12, 5, 4) == 1)
+
+#define MAX2175_IS_DAB_MODE(ctx) \
+ (max2175_read_bits(ctx, 12, 5, 4) == 2)
+
+static int max2175_band_from_freq(u32 freq)
+{
+ if (freq >= 144000 && freq <= 26100000)
+ return MAX2175_BAND_AM;
+ else if (freq >= 65000000 && freq <= 108000000)
+ return MAX2175_BAND_FM;
+
+ return MAX2175_BAND_VHF;
+}
+
+static void max2175_i2s_enable(struct max2175 *ctx, bool enable)
+{
+ if (enable)
+ /* Stuff bits are zeroed */
+ max2175_write_bits(ctx, 104, 3, 0, 2);
+ else
+ /* Keep SCK alive */
+ max2175_write_bits(ctx, 104, 3, 0, 9);
+ mxm_dbg(ctx, "i2s %sabled\n", enable ? "en" : "dis");
+}
+
+static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel,
+ u8 bank, const u16 *coeffs)
+{
+ unsigned int i;
+ u8 coeff_addr, upper_address = 24;
+
+ mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank);
+ max2175_write_bits(ctx, 114, 5, 4, m_sel);
+
+ if (m_sel == 2)
+ upper_address = 12;
+
+ for (i = 0; i < upper_address; i++) {
+ coeff_addr = i + bank * 24;
+ max2175_write(ctx, 115, coeffs[i] >> 8);
+ max2175_write(ctx, 116, coeffs[i]);
+ max2175_write(ctx, 117, coeff_addr | 1 << 7);
+ }
+ max2175_write_bit(ctx, 117, 7, 0);
+}
+
+static void max2175_load_fmeu_1p2(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++)
+ max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val);
+
+ ctx->decim_ratio = 36;
+
+ /* Load the Channel Filter Coefficients into channel filter bank #2 */
+ max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu);
+ max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+ eq_coeff_fmeu1_ra02_m6db);
+}
+
+static void max2175_load_dab_1p2(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
+ max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val);
+
+ ctx->decim_ratio = 1;
+
+ /* Load the Channel Filter Coefficients into channel filter bank #2 */
+ max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1);
+}
+
+static void max2175_load_fmna_1p0(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++)
+ max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val);
+}
+
+static void max2175_load_fmna_2p0(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++)
+ max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val);
+}
+
+static void max2175_set_bbfilter(struct max2175 *ctx)
+{
+ if (MAX2175_IS_BAND_AM(ctx)) {
+ max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
+ mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am);
+ } else if (MAX2175_IS_DAB_MODE(ctx)) {
+ max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
+ mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab);
+ } else {
+ max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
+ mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm);
+ }
+}
+
+static bool max2175_set_csm_mode(struct max2175 *ctx,
+ enum max2175_csm_mode new_mode)
+{
+ int ret = max2175_poll_csm_ready(ctx);
+
+ if (ret)
+ return ret;
+
+ max2175_write_bits(ctx, 0, 2, 0, new_mode);
+ mxm_dbg(ctx, "set csm new mode %d\n", new_mode);
+
+ /* Wait for a fixed settle down time depending on new mode */
+ switch (new_mode) {
+ case MAX2175_PRESET_TUNE:
+ usleep_range(51100, 51500); /* 51.1ms */
+ break;
+ /*
+ * Other mode switches need different sleep values depending on band &
+ * mode
+ */
+ default:
+ break;
+ }
+
+ return max2175_poll_csm_ready(ctx);
+}
+
+static int max2175_csm_action(struct max2175 *ctx,
+ enum max2175_csm_mode action)
+{
+ int ret;
+
+ mxm_dbg(ctx, "csm_action: %d\n", action);
+
+ /* Other actions can be added in future when needed */
+ ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER);
+ if (ret)
+ return ret;
+
+ return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE);
+}
+
+static int max2175_set_lo_freq(struct max2175 *ctx, u32 lo_freq)
+{
+ u8 lo_mult, loband_bits = 0, vcodiv_bits = 0;
+ u32 int_desired, frac_desired;
+ enum max2175_band band;
+ int ret;
+
+ band = max2175_read_bits(ctx, 5, 1, 0);
+ switch (band) {
+ case MAX2175_BAND_AM:
+ lo_mult = 16;
+ break;
+ case MAX2175_BAND_FM:
+ if (lo_freq <= 74700000) {
+ lo_mult = 16;
+ } else if (lo_freq > 74700000 && lo_freq <= 110000000) {
+ loband_bits = 1;
+ lo_mult = 8;
+ } else {
+ loband_bits = 1;
+ vcodiv_bits = 3;
+ lo_mult = 8;
+ }
+ break;
+ case MAX2175_BAND_VHF:
+ if (lo_freq <= 210000000)
+ vcodiv_bits = 2;
+ else
+ vcodiv_bits = 1;
+
+ loband_bits = 2;
+ lo_mult = 4;
+ break;
+ default:
+ loband_bits = 3;
+ vcodiv_bits = 2;
+ lo_mult = 2;
+ break;
+ }
+
+ if (band == MAX2175_BAND_L)
+ lo_freq /= lo_mult;
+ else
+ lo_freq *= lo_mult;
+
+ int_desired = lo_freq / ctx->xtal_freq;
+ frac_desired = div_u64((u64)(lo_freq % ctx->xtal_freq) << 20,
+ ctx->xtal_freq);
+
+ /* Check CSM is not busy */
+ ret = max2175_poll_csm_ready(ctx);
+ if (ret)
+ return ret;
+
+ mxm_dbg(ctx, "lo_mult %u int %u frac %u\n",
+ lo_mult, int_desired, frac_desired);
+
+ /* Write the calculated values to the appropriate registers */
+ max2175_write(ctx, 1, int_desired);
+ max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
+ max2175_write(ctx, 3, frac_desired >> 8);
+ max2175_write(ctx, 4, frac_desired);
+ max2175_write_bits(ctx, 5, 3, 2, loband_bits);
+ max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
+
+ return ret;
+}
+
+/*
+ * Helper similar to DIV_ROUND_CLOSEST but an inline function that accepts s64
+ * dividend and s32 divisor
+ */
+static inline s64 max2175_round_closest(s64 dividend, s32 divisor)
+{
+ if ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0))
+ return div_s64(dividend + divisor / 2, divisor);
+
+ return div_s64(dividend - divisor / 2, divisor);
+}
+
+static int max2175_set_nco_freq(struct max2175 *ctx, s32 nco_freq)
+{
+ s32 clock_rate = ctx->xtal_freq / ctx->decim_ratio;
+ u32 nco_reg, abs_nco_freq = abs(nco_freq);
+ s64 nco_val_desired;
+ int ret;
+
+ if (abs_nco_freq < clock_rate / 2) {
+ nco_val_desired = 2 * nco_freq;
+ } else {
+ nco_val_desired = 2 * (clock_rate - abs_nco_freq);
+ if (nco_freq < 0)
+ nco_val_desired = -nco_val_desired;
+ }
+
+ nco_reg = max2175_round_closest(nco_val_desired << 20, clock_rate);
+
+ if (nco_freq < 0)
+ nco_reg += 0x200000;
+
+ /* Check CSM is not busy */
+ ret = max2175_poll_csm_ready(ctx);
+ if (ret)
+ return ret;
+
+ mxm_dbg(ctx, "freq %d desired %lld reg %u\n",
+ nco_freq, nco_val_desired, nco_reg);
+
+ /* Write the calculated values to the appropriate registers */
+ max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f);
+ max2175_write(ctx, 8, nco_reg >> 8);
+ max2175_write(ctx, 9, nco_reg);
+
+ return ret;
+}
+
+static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq,
+ u32 lo_pos)
+{
+ s64 adj_freq, low_if_freq;
+ int ret;
+
+ mxm_dbg(ctx, "rf_freq: non AM bands\n");
+
+ if (MAX2175_IS_FM_MODE(ctx))
+ low_if_freq = 128000;
+ else if (MAX2175_IS_FMHD_MODE(ctx))
+ low_if_freq = 228000;
+ else
+ return max2175_set_lo_freq(ctx, freq);
+
+ if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED))
+ adj_freq = freq + low_if_freq;
+ else
+ adj_freq = freq - low_if_freq;
+
+ ret = max2175_set_lo_freq(ctx, adj_freq);
+ if (ret)
+ return ret;
+
+ return max2175_set_nco_freq(ctx, -low_if_freq);
+}
+
+static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos)
+{
+ int ret;
+
+ if (MAX2175_IS_BAND_AM(ctx))
+ ret = max2175_set_nco_freq(ctx, freq);
+ else
+ ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos);
+
+ mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq);
+
+ return ret;
+}
+
+static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls)
+{
+ int ret;
+
+ ret = max2175_set_rf_freq(ctx, freq, hsls);
+ if (ret)
+ return ret;
+
+ ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE);
+ if (ret)
+ return ret;
+
+ mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq);
+ ctx->freq = freq;
+
+ return ret;
+}
+
+static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos)
+{
+ mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos);
+
+ if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx))
+ max2175_write_bit(ctx, 5, 4, 1);
+ else
+ max2175_write_bit(ctx, 5, 4, 0);
+}
+
+static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+ switch (rx_mode) {
+ case MAX2175_EU_FM_1_2:
+ max2175_load_fmeu_1p2(ctx);
+ break;
+
+ case MAX2175_DAB_1_2:
+ max2175_load_dab_1p2(ctx);
+ break;
+ }
+ /* Master is the default setting */
+ if (!ctx->master)
+ max2175_write_bit(ctx, 30, 7, 1);
+}
+
+static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+ switch (rx_mode) {
+ case MAX2175_NA_FM_1_0:
+ max2175_load_fmna_1p0(ctx);
+ break;
+ case MAX2175_NA_FM_2_0:
+ max2175_load_fmna_2p0(ctx);
+ break;
+ }
+ /* Master is the default setting */
+ if (!ctx->master)
+ max2175_write_bit(ctx, 30, 7, 1);
+
+ ctx->decim_ratio = 27;
+
+ /* Load the Channel Filter Coefficients into channel filter bank #2 */
+ max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna);
+ max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+ eq_coeff_fmna1_ra02_m6db);
+}
+
+static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+ mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz);
+ if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+ max2175_set_eu_rx_mode(ctx, rx_mode);
+ else
+ max2175_set_na_rx_mode(ctx, rx_mode);
+
+ if (ctx->am_hiz) {
+ mxm_dbg(ctx, "setting AM HiZ related config\n");
+ max2175_write_bit(ctx, 50, 5, 1);
+ max2175_write_bit(ctx, 90, 7, 1);
+ max2175_write_bits(ctx, 73, 1, 0, 2);
+ max2175_write_bits(ctx, 80, 5, 0, 33);
+ }
+
+ /* Load BB filter trim values saved in ROM */
+ max2175_set_bbfilter(ctx);
+
+ /* Set HSLS */
+ max2175_set_hsls(ctx, ctx->hsls->cur.val);
+
+ /* Use i2s enable settings */
+ max2175_i2s_enable(ctx, ctx->i2s_en->cur.val);
+
+ ctx->mode_resolved = true;
+
+ return 0;
+}
+
+static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode)
+{
+ unsigned int i;
+ int band = max2175_band_from_freq(freq);
+
+ /* Pick the first match always */
+ for (i = 0; i <= ctx->rx_mode->maximum; i++) {
+ if (ctx->rx_modes[i].band == band) {
+ *mode = i;
+ mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n",
+ freq, *mode);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static bool max2175_freq_rx_mode_valid(struct max2175 *ctx,
+ u32 mode, u32 freq)
+{
+ int band = max2175_band_from_freq(freq);
+
+ return (ctx->rx_modes[mode].band == band);
+}
+
+static void max2175_load_adc_presets(struct max2175 *ctx)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(adc_presets); i++)
+ for (j = 0; j < ARRAY_SIZE(adc_presets[0]); j++)
+ max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]);
+}
+
+static int max2175_init_power_manager(struct max2175 *ctx)
+{
+ int ret;
+
+ /* Execute on-chip power-up/calibration */
+ max2175_write_bit(ctx, 99, 2, 0);
+ usleep_range(1000, 1500);
+ max2175_write_bit(ctx, 99, 2, 1);
+
+ /* Wait for the power manager to finish. */
+ ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50000);
+ if (ret)
+ mxm_err(ctx, "init pm failed\n");
+
+ return ret;
+}
+
+static int max2175_recalibrate_adc(struct max2175 *ctx)
+{
+ int ret;
+
+ /* ADC Re-calibration */
+ max2175_write(ctx, 150, 0xff);
+ max2175_write(ctx, 205, 0xff);
+ max2175_write(ctx, 147, 0x20);
+ max2175_write(ctx, 147, 0x00);
+ max2175_write(ctx, 202, 0x20);
+ max2175_write(ctx, 202, 0x00);
+
+ ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50000);
+ if (ret)
+ mxm_err(ctx, "adc recalibration failed\n");
+
+ return ret;
+}
+
+static u8 max2175_read_rom(struct max2175 *ctx, u8 row)
+{
+ u8 data = 0;
+
+ max2175_write_bit(ctx, 56, 4, 0);
+ max2175_write_bits(ctx, 56, 3, 0, row);
+
+ usleep_range(2000, 2500);
+ max2175_read(ctx, 58, &data);
+
+ max2175_write_bits(ctx, 56, 3, 0, 0);
+
+ mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data);
+
+ return data;
+}
+
+static void max2175_load_from_rom(struct max2175 *ctx)
+{
+ u8 data = 0;
+
+ data = max2175_read_rom(ctx, 0);
+ ctx->rom_bbf_bw_am = data & 0x0f;
+ max2175_write_bits(ctx, 81, 3, 0, data >> 4);
+
+ data = max2175_read_rom(ctx, 1);
+ ctx->rom_bbf_bw_fm = data & 0x0f;
+ ctx->rom_bbf_bw_dab = data >> 4;
+
+ data = max2175_read_rom(ctx, 2);
+ max2175_write_bits(ctx, 82, 4, 0, data & 0x1f);
+ max2175_write_bits(ctx, 82, 7, 5, data >> 5);
+
+ data = max2175_read_rom(ctx, 3);
+ if (ctx->am_hiz) {
+ data &= 0x0f;
+ data |= (max2175_read_rom(ctx, 7) & 0x40) >> 2;
+ if (!data)
+ data |= 2;
+ } else {
+ data = (data & 0xf0) >> 4;
+ data |= (max2175_read_rom(ctx, 7) & 0x80) >> 3;
+ if (!data)
+ data |= 30;
+ }
+ max2175_write_bits(ctx, 80, 5, 0, data + 31);
+
+ data = max2175_read_rom(ctx, 6);
+ max2175_write_bits(ctx, 81, 7, 6, data >> 6);
+}
+
+static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++)
+ max2175_write(ctx, i + 1, full_fm_eu_1p0[i]);
+
+ usleep_range(5000, 5500);
+ ctx->decim_ratio = 36;
+}
+
+static void max2175_load_full_fm_na_1p0(struct max2175 *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++)
+ max2175_write(ctx, i + 1, full_fm_na_1p0[i]);
+
+ usleep_range(5000, 5500);
+ ctx->decim_ratio = 27;
+}
+
+static int max2175_core_init(struct max2175 *ctx, u32 refout_bits)
+{
+ int ret;
+
+ /* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
+ if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+ max2175_load_full_fm_eu_1p0(ctx);
+ else
+ max2175_load_full_fm_na_1p0(ctx);
+
+ /* The default settings assume master */
+ if (!ctx->master)
+ max2175_write_bit(ctx, 30, 7, 1);
+
+ mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
+
+ /* Set REFOUT */
+ max2175_write_bits(ctx, 56, 7, 5, refout_bits);
+
+ /* ADC Reset */
+ max2175_write_bit(ctx, 99, 1, 0);
+ usleep_range(1000, 1500);
+ max2175_write_bit(ctx, 99, 1, 1);
+
+ /* Load ADC preset values */
+ max2175_load_adc_presets(ctx);
+
+ /* Initialize the power management state machine */
+ ret = max2175_init_power_manager(ctx);
+ if (ret)
+ return ret;
+
+ /* Recalibrate ADC */
+ ret = max2175_recalibrate_adc(ctx);
+ if (ret)
+ return ret;
+
+ /* Load ROM values to appropriate registers */
+ max2175_load_from_rom(ctx);
+
+ if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+ /* Load FIR coefficients into bank 0 */
+ max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+ ch_coeff_fmeu);
+ max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+ eq_coeff_fmeu1_ra02_m6db);
+ } else {
+ /* Load FIR coefficients into bank 0 */
+ max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+ ch_coeff_fmna);
+ max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+ eq_coeff_fmna1_ra02_m6db);
+ }
+ mxm_dbg(ctx, "core initialized\n");
+
+ return 0;
+}
+
+static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+ /* Load mode. Range check already done */
+ max2175_set_rx_mode(ctx, rx_mode);
+
+ mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq);
+
+ /* Check if current freq valid for mode & update */
+ if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq))
+ max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->cur.val);
+ else
+ /* Use default freq of mode if current freq is not valid */
+ max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq,
+ ctx->hsls->cur.val);
+}
+
+static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+ mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val);
+ switch (ctrl->id) {
+ case V4L2_CID_MAX2175_I2S_ENABLE:
+ max2175_i2s_enable(ctx, ctrl->val);
+ break;
+ case V4L2_CID_MAX2175_HSLS:
+ max2175_set_hsls(ctx, ctrl->val);
+ break;
+ case V4L2_CID_MAX2175_RX_MODE:
+ max2175_s_ctrl_rx_mode(ctx, ctrl->val);
+ break;
+ }
+
+ return 0;
+}
+
+static u32 max2175_get_lna_gain(struct max2175 *ctx)
+{
+ enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0);
+
+ switch (band) {
+ case MAX2175_BAND_AM:
+ return max2175_read_bits(ctx, 51, 3, 0);
+ case MAX2175_BAND_FM:
+ return max2175_read_bits(ctx, 50, 3, 0);
+ case MAX2175_BAND_VHF:
+ return max2175_read_bits(ctx, 52, 5, 0);
+ default:
+ return 0;
+ }
+}
+
+static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_LNA_GAIN:
+ ctrl->val = max2175_get_lna_gain(ctx);
+ break;
+ case V4L2_CID_RF_TUNER_IF_GAIN:
+ ctrl->val = max2175_read_bits(ctx, 49, 4, 0);
+ break;
+ case V4L2_CID_RF_TUNER_PLL_LOCK:
+ ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3);
+ break;
+ }
+
+ return 0;
+};
+
+static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq)
+{
+ u32 rx_mode;
+ int ret;
+
+ /* Get band from frequency */
+ ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode);
+ if (ret)
+ return ret;
+
+ mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
+
+ /* Load mode */
+ max2175_set_rx_mode(ctx, rx_mode);
+ ctx->rx_mode->cur.val = rx_mode;
+
+ /* Tune to the new freq given */
+ return max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val);
+}
+
+static int max2175_s_frequency(struct v4l2_subdev *sd,
+ const struct v4l2_frequency *vf)
+{
+ struct max2175 *ctx = max2175_from_sd(sd);
+ u32 freq;
+ int ret = 0;
+
+ mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n",
+ vf->frequency, ctx->freq, ctx->mode_resolved);
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ freq = clamp(vf->frequency, ctx->bands_rf->rangelow,
+ ctx->bands_rf->rangehigh);
+
+ /* Check new freq valid for rx_mode if already resolved */
+ if (ctx->mode_resolved &&
+ max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->cur.val, freq))
+ ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val);
+ else
+ /* Find default rx_mode for freq and tune to it */
+ ret = max2175_set_freq_and_mode(ctx, freq);
+
+ mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n",
+ ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->cur.val);
+
+ return ret;
+}
+
+static int max2175_g_frequency(struct v4l2_subdev *sd,
+ struct v4l2_frequency *vf)
+{
+ struct max2175 *ctx = max2175_from_sd(sd);
+ int ret = 0;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ /* RF freq */
+ vf->type = V4L2_TUNER_RF;
+ vf->frequency = ctx->freq;
+
+ return ret;
+}
+
+static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
+ struct v4l2_frequency_band *band)
+{
+ struct max2175 *ctx = max2175_from_sd(sd);
+
+ if (band->tuner != 0 || band->index != 0)
+ return -EINVAL;
+
+ *band = *ctx->bands_rf;
+
+ return 0;
+}
+
+static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct max2175 *ctx = max2175_from_sd(sd);
+
+ if (vt->index > 0)
+ return -EINVAL;
+
+ strlcpy(vt->name, "RF", sizeof(vt->name));
+ vt->type = V4L2_TUNER_RF;
+ vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ vt->rangelow = ctx->bands_rf->rangelow;
+ vt->rangehigh = ctx->bands_rf->rangehigh;
+
+ return 0;
+}
+
+static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
+{
+ /* Check tuner index is valid */
+ if (vt->index > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
+ .s_frequency = max2175_s_frequency,
+ .g_frequency = max2175_g_frequency,
+ .enum_freq_bands = max2175_enum_freq_bands,
+ .g_tuner = max2175_g_tuner,
+ .s_tuner = max2175_s_tuner,
+};
+
+static const struct v4l2_subdev_ops max2175_ops = {
+ .tuner = &max2175_tuner_ops,
+};
+
+static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
+ .s_ctrl = max2175_s_ctrl,
+ .g_volatile_ctrl = max2175_g_volatile_ctrl,
+};
+
+/*
+ * I2S output enable/disable configuration. This is a private control.
+ * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ */
+static const struct v4l2_ctrl_config max2175_i2s_en = {
+ .ops = &max2175_ctrl_ops,
+ .id = V4L2_CID_MAX2175_I2S_ENABLE,
+ .name = "I2S Enable",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .is_private = 1,
+};
+
+/*
+ * HSLS value control LO freq adjacent location configuration.
+ * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ */
+static const struct v4l2_ctrl_config max2175_hsls = {
+ .ops = &max2175_ctrl_ops,
+ .id = V4L2_CID_MAX2175_HSLS,
+ .name = "HSLS Above/Below Desired",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+};
+
+/*
+ * Rx modes below are a set of preset configurations that decides the tuner's
+ * sck and sample rate of transmission. They are separate for EU & NA regions.
+ * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ */
+static const char * const max2175_ctrl_eu_rx_modes[] = {
+ [MAX2175_EU_FM_1_2] = "EU FM 1.2",
+ [MAX2175_DAB_1_2] = "DAB 1.2",
+};
+
+static const char * const max2175_ctrl_na_rx_modes[] = {
+ [MAX2175_NA_FM_1_0] = "NA FM 1.0",
+ [MAX2175_NA_FM_2_0] = "NA FM 2.0",
+};
+
+static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
+ .ops = &max2175_ctrl_ops,
+ .id = V4L2_CID_MAX2175_RX_MODE,
+ .name = "RX Mode",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
+ .def = 0,
+ .qmenu = max2175_ctrl_eu_rx_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_na_rx_mode = {
+ .ops = &max2175_ctrl_ops,
+ .id = V4L2_CID_MAX2175_RX_MODE,
+ .name = "RX Mode",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
+ .def = 0,
+ .qmenu = max2175_ctrl_na_rx_modes,
+};
+
+static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load,
+ u32 *bits)
+{
+ if (load <= 40)
+ *bits = load / 10;
+ else if (load >= 60 && load <= 70)
+ *bits = load / 10 - 1;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int max2175_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ bool master = true, am_hiz = false;
+ u32 refout_load, refout_bits = 0; /* REFOUT disabled */
+ struct v4l2_ctrl_handler *hdl;
+ struct fwnode_handle *fwnode;
+ struct device_node *np;
+ struct v4l2_subdev *sd;
+ struct regmap *regmap;
+ struct max2175 *ctx;
+ struct clk *clk;
+ int ret;
+
+ /* Parse DT properties */
+ np = of_parse_phandle(client->dev.of_node, "maxim,master", 0);
+ if (np) {
+ master = false; /* Slave tuner */
+ of_node_put(np);
+ }
+
+ fwnode = of_fwnode_handle(client->dev.of_node);
+ if (fwnode_property_present(fwnode, "maxim,am-hiz-filter"))
+ am_hiz = true;
+
+ if (!fwnode_property_read_u32(fwnode, "maxim,refout-load",
+ &refout_load)) {
+ ret = max2175_refout_load_to_bits(client, refout_load,
+ &refout_bits);
+ if (ret) {
+ dev_err(&client->dev, "invalid refout_load %u\n",
+ refout_load);
+ return -EINVAL;
+ }
+ }
+
+ clk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(&client->dev, "cannot get clock %d\n", ret);
+ return -ENODEV;
+ }
+
+ regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "regmap init failed %d\n", ret);
+ return -ENODEV;
+ }
+
+ /* Alloc tuner context */
+ ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
+ if (ctx == NULL)
+ return -ENOMEM;
+
+ sd = &ctx->sd;
+ ctx->master = master;
+ ctx->am_hiz = am_hiz;
+ ctx->mode_resolved = false;
+ ctx->regmap = regmap;
+ ctx->xtal_freq = clk_get_rate(clk);
+ dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq);
+
+ v4l2_i2c_subdev_init(sd, client, &max2175_ops);
+ ctx->client = client;
+
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ /* Controls */
+ hdl = &ctx->ctrl_hdl;
+ ret = v4l2_ctrl_handler_init(hdl, 7);
+ if (ret)
+ return ret;
+
+ ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+ V4L2_CID_RF_TUNER_LNA_GAIN,
+ 0, 63, 1, 0);
+ ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY);
+ ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+ V4L2_CID_RF_TUNER_IF_GAIN,
+ 0, 31, 1, 0);
+ ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY);
+ ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+ V4L2_CID_RF_TUNER_PLL_LOCK,
+ 0, 1, 1, 0);
+ ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY);
+ ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL);
+ ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL);
+
+ if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+ ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+ &max2175_eu_rx_mode, NULL);
+ ctx->rx_modes = eu_rx_modes;
+ ctx->bands_rf = &eu_bands_rf;
+ } else {
+ ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+ &max2175_na_rx_mode, NULL);
+ ctx->rx_modes = na_rx_modes;
+ ctx->bands_rf = &na_bands_rf;
+ }
+ ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
+
+ /* Set the defaults */
+ ctx->freq = ctx->bands_rf->rangelow;
+
+ /* Register subdev */
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(&client->dev, "register subdev failed\n");
+ goto err_reg;
+ }
+
+ /* Initialize device */
+ ret = max2175_core_init(ctx, refout_bits);
+ if (ret)
+ goto err_init;
+
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret)
+ goto err_init;
+
+ return 0;
+
+err_init:
+ v4l2_async_unregister_subdev(sd);
+err_reg:
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+
+ return ret;
+}
+
+static int max2175_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct max2175 *ctx = max2175_from_sd(sd);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ v4l2_async_unregister_subdev(sd);
+
+ return 0;
+}
+
+static const struct i2c_device_id max2175_id[] = {
+ { DRIVER_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, max2175_id);
+
+static const struct of_device_id max2175_of_ids[] = {
+ { .compatible = "maxim,max2175", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max2175_of_ids);
+
+static struct i2c_driver max2175_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = max2175_of_ids,
+ },
+ .probe = max2175_probe,
+ .remove = max2175_remove,
+ .id_table = max2175_id,
+};
+
+module_i2c_driver(max2175_driver);
+
+MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
diff --git a/drivers/media/i2c/max2175.h b/drivers/media/i2c/max2175.h
new file mode 100644
index 000000000000..eb43373ce7e2
--- /dev/null
+++ b/drivers/media/i2c/max2175.h
@@ -0,0 +1,109 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this device.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2017 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MAX2175_H__
+#define __MAX2175_H__
+
+#define MAX2175_EU_XTAL_FREQ 36864000 /* In Hz */
+#define MAX2175_NA_XTAL_FREQ 40186125 /* In Hz */
+
+enum max2175_region {
+ MAX2175_REGION_EU = 0, /* Europe */
+ MAX2175_REGION_NA, /* North America */
+};
+
+enum max2175_band {
+ MAX2175_BAND_AM = 0,
+ MAX2175_BAND_FM,
+ MAX2175_BAND_VHF,
+ MAX2175_BAND_L,
+};
+
+enum max2175_eu_mode {
+ /* EU modes */
+ MAX2175_EU_FM_1_2 = 0,
+ MAX2175_DAB_1_2,
+
+ /*
+ * Other possible modes to add in future
+ * MAX2175_DAB_1_0,
+ * MAX2175_DAB_1_3,
+ * MAX2175_EU_FM_2_2,
+ * MAX2175_EU_FMHD_4_0,
+ * MAX2175_EU_AM_1_0,
+ * MAX2175_EU_AM_2_2,
+ */
+};
+
+enum max2175_na_mode {
+ /* NA modes */
+ MAX2175_NA_FM_1_0 = 0,
+ MAX2175_NA_FM_2_0,
+
+ /*
+ * Other possible modes to add in future
+ * MAX2175_NA_FMHD_1_0,
+ * MAX2175_NA_FMHD_1_2,
+ * MAX2175_NA_AM_1_0,
+ * MAX2175_NA_AM_1_2,
+ */
+};
+
+/* Supported I2S modes */
+enum {
+ MAX2175_I2S_MODE0 = 0,
+ MAX2175_I2S_MODE1,
+ MAX2175_I2S_MODE2,
+ MAX2175_I2S_MODE3,
+ MAX2175_I2S_MODE4,
+};
+
+/* Coefficient table groups */
+enum {
+ MAX2175_CH_MSEL = 0,
+ MAX2175_EQ_MSEL,
+ MAX2175_AA_MSEL,
+};
+
+/* HSLS LO injection polarity */
+enum {
+ MAX2175_LO_BELOW_DESIRED = 0,
+ MAX2175_LO_ABOVE_DESIRED,
+};
+
+/* Channel FSM modes */
+enum max2175_csm_mode {
+ MAX2175_LOAD_TO_BUFFER = 0,
+ MAX2175_PRESET_TUNE,
+ MAX2175_SEARCH,
+ MAX2175_AF_UPDATE,
+ MAX2175_JUMP_FAST_TUNE,
+ MAX2175_CHECK,
+ MAX2175_LOAD_AND_SWAP,
+ MAX2175_END,
+ MAX2175_BUFFER_PLUS_PRESET_TUNE,
+ MAX2175_BUFFER_PLUS_SEARCH,
+ MAX2175_BUFFER_PLUS_AF_UPDATE,
+ MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE,
+ MAX2175_BUFFER_PLUS_CHECK,
+ MAX2175_BUFFER_PLUS_LOAD_AND_SWAP,
+ MAX2175_NO_ACTION
+};
+
+#endif /* __MAX2175_H__ */
diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c
index 11fc593ed908..4dd01e9f553b 100644
--- a/drivers/media/i2c/msp3400-kthreads.c
+++ b/drivers/media/i2c/msp3400-kthreads.c
@@ -655,6 +655,7 @@ restart:
break;
case 0: /* 4.5 */
state->detected_std = V4L2_STD_MN;
+ /* fall-through */
default:
no_second:
state->second = msp3400c_carrier_detect_main[max1].cdo;
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 2e7a6e62a358..8a430640c85d 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -19,6 +19,7 @@
#include <linux/log2.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
@@ -28,7 +29,7 @@
#include <media/i2c/mt9v032.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
/* The first four rows are black rows. The active area spans 753x481 pixels. */
@@ -979,7 +980,7 @@ static struct mt9v032_platform_data *
mt9v032_get_pdata(struct i2c_client *client)
{
struct mt9v032_platform_data *pdata = NULL;
- struct v4l2_of_endpoint endpoint;
+ struct v4l2_fwnode_endpoint endpoint;
struct device_node *np;
struct property *prop;
@@ -990,7 +991,7 @@ mt9v032_get_pdata(struct i2c_client *client)
if (!np)
return NULL;
- if (v4l2_of_parse_endpoint(np, &endpoint) < 0)
+ if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0)
goto done;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
new file mode 100644
index 000000000000..86550d8ddfee
--- /dev/null
+++ b/drivers/media/i2c/ov13858.c
@@ -0,0 +1,1816 @@
+/*
+ * Copyright (c) 2017 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define OV13858_REG_VALUE_08BIT 1
+#define OV13858_REG_VALUE_16BIT 2
+#define OV13858_REG_VALUE_24BIT 3
+
+#define OV13858_REG_MODE_SELECT 0x0100
+#define OV13858_MODE_STANDBY 0x00
+#define OV13858_MODE_STREAMING 0x01
+
+#define OV13858_REG_SOFTWARE_RST 0x0103
+#define OV13858_SOFTWARE_RST 0x01
+
+/* PLL1 generates PCLK and MIPI_PHY_CLK */
+#define OV13858_REG_PLL1_CTRL_0 0x0300
+#define OV13858_REG_PLL1_CTRL_1 0x0301
+#define OV13858_REG_PLL1_CTRL_2 0x0302
+#define OV13858_REG_PLL1_CTRL_3 0x0303
+#define OV13858_REG_PLL1_CTRL_4 0x0304
+#define OV13858_REG_PLL1_CTRL_5 0x0305
+
+/* PLL2 generates DAC_CLK, SCLK and SRAM_CLK */
+#define OV13858_REG_PLL2_CTRL_B 0x030b
+#define OV13858_REG_PLL2_CTRL_C 0x030c
+#define OV13858_REG_PLL2_CTRL_D 0x030d
+#define OV13858_REG_PLL2_CTRL_E 0x030e
+#define OV13858_REG_PLL2_CTRL_F 0x030f
+#define OV13858_REG_PLL2_CTRL_12 0x0312
+#define OV13858_REG_MIPI_SC_CTRL0 0x3016
+#define OV13858_REG_MIPI_SC_CTRL1 0x3022
+
+/* Chip ID */
+#define OV13858_REG_CHIP_ID 0x300a
+#define OV13858_CHIP_ID 0x00d855
+
+/* V_TIMING internal */
+#define OV13858_REG_VTS 0x380e
+#define OV13858_VTS_30FPS 0x0c8e /* 30 fps */
+#define OV13858_VTS_60FPS 0x0648 /* 60 fps */
+#define OV13858_VTS_MAX 0x7fff
+#define OV13858_VBLANK_MIN 56
+
+/* HBLANK control - read only */
+#define OV13858_PPL_540MHZ 2244
+#define OV13858_PPL_1080MHZ 4488
+
+/* Exposure control */
+#define OV13858_REG_EXPOSURE 0x3500
+#define OV13858_EXPOSURE_MIN 4
+#define OV13858_EXPOSURE_MAX (OV13858_VTS_MAX - 8)
+#define OV13858_EXPOSURE_STEP 1
+#define OV13858_EXPOSURE_DEFAULT 0x640
+
+/* Analog gain control */
+#define OV13858_REG_ANALOG_GAIN 0x3508
+#define OV13858_ANA_GAIN_MIN 0
+#define OV13858_ANA_GAIN_MAX 0x1fff
+#define OV13858_ANA_GAIN_STEP 1
+#define OV13858_ANA_GAIN_DEFAULT 0x80
+
+/* Digital gain control */
+#define OV13858_REG_DIGITAL_GAIN 0x350a
+#define OV13858_DGTL_GAIN_MASK 0xf3
+#define OV13858_DGTL_GAIN_SHIFT 2
+#define OV13858_DGTL_GAIN_MIN 1
+#define OV13858_DGTL_GAIN_MAX 4
+#define OV13858_DGTL_GAIN_STEP 1
+#define OV13858_DGTL_GAIN_DEFAULT 1
+
+/* Test Pattern Control */
+#define OV13858_REG_TEST_PATTERN 0x4503
+#define OV13858_TEST_PATTERN_ENABLE BIT(7)
+#define OV13858_TEST_PATTERN_MASK 0xfc
+
+/* Number of frames to skip */
+#define OV13858_NUM_OF_SKIP_FRAMES 2
+
+struct ov13858_reg {
+ u16 address;
+ u8 val;
+};
+
+struct ov13858_reg_list {
+ u32 num_of_regs;
+ const struct ov13858_reg *regs;
+};
+
+/* Link frequency config */
+struct ov13858_link_freq_config {
+ u32 pixel_rate;
+ u32 pixels_per_line;
+
+ /* PLL registers for this link frequency */
+ struct ov13858_reg_list reg_list;
+};
+
+/* Mode : resolution and related config&values */
+struct ov13858_mode {
+ /* Frame width */
+ u32 width;
+ /* Frame height */
+ u32 height;
+
+ /* V-timing */
+ u32 vts;
+
+ /* Index of Link frequency config to be used */
+ u32 link_freq_index;
+ /* Default register values */
+ struct ov13858_reg_list reg_list;
+};
+
+/* 4224x3136 needs 1080Mbps/lane, 4 lanes */
+static const struct ov13858_reg mipi_data_rate_1080mbps[] = {
+ /* PLL1 registers */
+ {OV13858_REG_PLL1_CTRL_0, 0x07},
+ {OV13858_REG_PLL1_CTRL_1, 0x01},
+ {OV13858_REG_PLL1_CTRL_2, 0xc2},
+ {OV13858_REG_PLL1_CTRL_3, 0x00},
+ {OV13858_REG_PLL1_CTRL_4, 0x00},
+ {OV13858_REG_PLL1_CTRL_5, 0x01},
+
+ /* PLL2 registers */
+ {OV13858_REG_PLL2_CTRL_B, 0x05},
+ {OV13858_REG_PLL2_CTRL_C, 0x01},
+ {OV13858_REG_PLL2_CTRL_D, 0x0e},
+ {OV13858_REG_PLL2_CTRL_E, 0x05},
+ {OV13858_REG_PLL2_CTRL_F, 0x01},
+ {OV13858_REG_PLL2_CTRL_12, 0x01},
+ {OV13858_REG_MIPI_SC_CTRL0, 0x72},
+ {OV13858_REG_MIPI_SC_CTRL1, 0x01},
+};
+
+/*
+ * 2112x1568, 2112x1188, 1056x784 need 540Mbps/lane,
+ * 4 lanes
+ */
+static const struct ov13858_reg mipi_data_rate_540mbps[] = {
+ /* PLL1 registers */
+ {OV13858_REG_PLL1_CTRL_0, 0x07},
+ {OV13858_REG_PLL1_CTRL_1, 0x01},
+ {OV13858_REG_PLL1_CTRL_2, 0xc2},
+ {OV13858_REG_PLL1_CTRL_3, 0x01},
+ {OV13858_REG_PLL1_CTRL_4, 0x00},
+ {OV13858_REG_PLL1_CTRL_5, 0x01},
+
+ /* PLL2 registers */
+ {OV13858_REG_PLL2_CTRL_B, 0x05},
+ {OV13858_REG_PLL2_CTRL_C, 0x01},
+ {OV13858_REG_PLL2_CTRL_D, 0x0e},
+ {OV13858_REG_PLL2_CTRL_E, 0x05},
+ {OV13858_REG_PLL2_CTRL_F, 0x01},
+ {OV13858_REG_PLL2_CTRL_12, 0x01},
+ {OV13858_REG_MIPI_SC_CTRL0, 0x72},
+ {OV13858_REG_MIPI_SC_CTRL1, 0x01},
+};
+
+static const struct ov13858_reg mode_4224x3136_regs[] = {
+ {0x3013, 0x32},
+ {0x301b, 0xf0},
+ {0x301f, 0xd0},
+ {0x3106, 0x15},
+ {0x3107, 0x23},
+ {0x350a, 0x00},
+ {0x350e, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x2b},
+ {0x3601, 0x52},
+ {0x3602, 0x60},
+ {0x3612, 0x05},
+ {0x3613, 0xa4},
+ {0x3620, 0x80},
+ {0x3621, 0x10},
+ {0x3622, 0x30},
+ {0x3624, 0x1c},
+ {0x3640, 0x10},
+ {0x3641, 0x70},
+ {0x3661, 0x80},
+ {0x3662, 0x12},
+ {0x3664, 0x73},
+ {0x3665, 0xa7},
+ {0x366e, 0xff},
+ {0x366f, 0xf4},
+ {0x3674, 0x00},
+ {0x3679, 0x0c},
+ {0x367f, 0x01},
+ {0x3680, 0x0c},
+ {0x3681, 0x50},
+ {0x3682, 0x50},
+ {0x3683, 0xa9},
+ {0x3684, 0xa9},
+ {0x3709, 0x5f},
+ {0x3714, 0x24},
+ {0x371a, 0x3e},
+ {0x3737, 0x04},
+ {0x3738, 0xcc},
+ {0x3739, 0x12},
+ {0x373d, 0x26},
+ {0x3764, 0x20},
+ {0x3765, 0x20},
+ {0x37a1, 0x36},
+ {0x37a8, 0x3b},
+ {0x37ab, 0x31},
+ {0x37c2, 0x04},
+ {0x37c3, 0xf1},
+ {0x37c5, 0x00},
+ {0x37d8, 0x03},
+ {0x37d9, 0x0c},
+ {0x37da, 0xc2},
+ {0x37dc, 0x02},
+ {0x37e0, 0x00},
+ {0x37e1, 0x0a},
+ {0x37e2, 0x14},
+ {0x37e3, 0x04},
+ {0x37e4, 0x2a},
+ {0x37e5, 0x03},
+ {0x37e6, 0x04},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x10},
+ {0x3805, 0x9f},
+ {0x3806, 0x0c},
+ {0x3807, 0x5f},
+ {0x3808, 0x10},
+ {0x3809, 0x80},
+ {0x380a, 0x0c},
+ {0x380b, 0x40},
+ {0x380c, 0x04},
+ {0x380d, 0x62},
+ {0x380e, 0x0c},
+ {0x380f, 0x8e},
+ {0x3811, 0x04},
+ {0x3813, 0x05},
+ {0x3814, 0x01},
+ {0x3815, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0xa8},
+ {0x3821, 0x00},
+ {0x3822, 0xc2},
+ {0x3823, 0x18},
+ {0x3826, 0x11},
+ {0x3827, 0x1c},
+ {0x3829, 0x03},
+ {0x3832, 0x00},
+ {0x3c80, 0x00},
+ {0x3c87, 0x01},
+ {0x3c8c, 0x19},
+ {0x3c8d, 0x1c},
+ {0x3c90, 0x00},
+ {0x3c91, 0x00},
+ {0x3c92, 0x00},
+ {0x3c93, 0x00},
+ {0x3c94, 0x40},
+ {0x3c95, 0x54},
+ {0x3c96, 0x34},
+ {0x3c97, 0x04},
+ {0x3c98, 0x00},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xc0},
+ {0x3f00, 0x0b},
+ {0x3f03, 0x00},
+ {0x4001, 0xe0},
+ {0x4008, 0x00},
+ {0x4009, 0x0f},
+ {0x4011, 0xf0},
+ {0x4017, 0x08},
+ {0x4050, 0x04},
+ {0x4051, 0x0b},
+ {0x4052, 0x00},
+ {0x4053, 0x80},
+ {0x4054, 0x00},
+ {0x4055, 0x80},
+ {0x4056, 0x00},
+ {0x4057, 0x80},
+ {0x4058, 0x00},
+ {0x4059, 0x80},
+ {0x405e, 0x20},
+ {0x4500, 0x07},
+ {0x4503, 0x00},
+ {0x450a, 0x04},
+ {0x4809, 0x04},
+ {0x480c, 0x12},
+ {0x481f, 0x30},
+ {0x4833, 0x10},
+ {0x4837, 0x0e},
+ {0x4902, 0x01},
+ {0x4d00, 0x03},
+ {0x4d01, 0xc9},
+ {0x4d02, 0xbc},
+ {0x4d03, 0xd7},
+ {0x4d04, 0xf0},
+ {0x4d05, 0xa2},
+ {0x5000, 0xfd},
+ {0x5001, 0x01},
+ {0x5040, 0x39},
+ {0x5041, 0x10},
+ {0x5042, 0x10},
+ {0x5043, 0x84},
+ {0x5044, 0x62},
+ {0x5180, 0x00},
+ {0x5181, 0x10},
+ {0x5182, 0x02},
+ {0x5183, 0x0f},
+ {0x5200, 0x1b},
+ {0x520b, 0x07},
+ {0x520c, 0x0f},
+ {0x5300, 0x04},
+ {0x5301, 0x0c},
+ {0x5302, 0x0c},
+ {0x5303, 0x0f},
+ {0x5304, 0x00},
+ {0x5305, 0x70},
+ {0x5306, 0x00},
+ {0x5307, 0x80},
+ {0x5308, 0x00},
+ {0x5309, 0xa5},
+ {0x530a, 0x00},
+ {0x530b, 0xd3},
+ {0x530c, 0x00},
+ {0x530d, 0xf0},
+ {0x530e, 0x01},
+ {0x530f, 0x10},
+ {0x5310, 0x01},
+ {0x5311, 0x20},
+ {0x5312, 0x01},
+ {0x5313, 0x20},
+ {0x5314, 0x01},
+ {0x5315, 0x20},
+ {0x5316, 0x08},
+ {0x5317, 0x08},
+ {0x5318, 0x10},
+ {0x5319, 0x88},
+ {0x531a, 0x88},
+ {0x531b, 0xa9},
+ {0x531c, 0xaa},
+ {0x531d, 0x0a},
+ {0x5405, 0x02},
+ {0x5406, 0x67},
+ {0x5407, 0x01},
+ {0x5408, 0x4a},
+};
+
+static const struct ov13858_reg mode_2112x1568_regs[] = {
+ {0x3013, 0x32},
+ {0x301b, 0xf0},
+ {0x301f, 0xd0},
+ {0x3106, 0x15},
+ {0x3107, 0x23},
+ {0x350a, 0x00},
+ {0x350e, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x2b},
+ {0x3601, 0x52},
+ {0x3602, 0x60},
+ {0x3612, 0x05},
+ {0x3613, 0xa4},
+ {0x3620, 0x80},
+ {0x3621, 0x10},
+ {0x3622, 0x30},
+ {0x3624, 0x1c},
+ {0x3640, 0x10},
+ {0x3641, 0x70},
+ {0x3661, 0x80},
+ {0x3662, 0x10},
+ {0x3664, 0x73},
+ {0x3665, 0xa7},
+ {0x366e, 0xff},
+ {0x366f, 0xf4},
+ {0x3674, 0x00},
+ {0x3679, 0x0c},
+ {0x367f, 0x01},
+ {0x3680, 0x0c},
+ {0x3681, 0x50},
+ {0x3682, 0x50},
+ {0x3683, 0xa9},
+ {0x3684, 0xa9},
+ {0x3709, 0x5f},
+ {0x3714, 0x28},
+ {0x371a, 0x3e},
+ {0x3737, 0x08},
+ {0x3738, 0xcc},
+ {0x3739, 0x20},
+ {0x373d, 0x26},
+ {0x3764, 0x20},
+ {0x3765, 0x20},
+ {0x37a1, 0x36},
+ {0x37a8, 0x3b},
+ {0x37ab, 0x31},
+ {0x37c2, 0x14},
+ {0x37c3, 0xf1},
+ {0x37c5, 0x00},
+ {0x37d8, 0x03},
+ {0x37d9, 0x0c},
+ {0x37da, 0xc2},
+ {0x37dc, 0x02},
+ {0x37e0, 0x00},
+ {0x37e1, 0x0a},
+ {0x37e2, 0x14},
+ {0x37e3, 0x08},
+ {0x37e4, 0x38},
+ {0x37e5, 0x03},
+ {0x37e6, 0x08},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x10},
+ {0x3805, 0x9f},
+ {0x3806, 0x0c},
+ {0x3807, 0x5f},
+ {0x3808, 0x08},
+ {0x3809, 0x40},
+ {0x380a, 0x06},
+ {0x380b, 0x20},
+ {0x380c, 0x04},
+ {0x380d, 0x62},
+ {0x380e, 0x0c},
+ {0x380f, 0x8e},
+ {0x3811, 0x04},
+ {0x3813, 0x05},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3820, 0xab},
+ {0x3821, 0x00},
+ {0x3822, 0xc2},
+ {0x3823, 0x18},
+ {0x3826, 0x04},
+ {0x3827, 0x90},
+ {0x3829, 0x07},
+ {0x3832, 0x00},
+ {0x3c80, 0x00},
+ {0x3c87, 0x01},
+ {0x3c8c, 0x19},
+ {0x3c8d, 0x1c},
+ {0x3c90, 0x00},
+ {0x3c91, 0x00},
+ {0x3c92, 0x00},
+ {0x3c93, 0x00},
+ {0x3c94, 0x40},
+ {0x3c95, 0x54},
+ {0x3c96, 0x34},
+ {0x3c97, 0x04},
+ {0x3c98, 0x00},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xc0},
+ {0x3f00, 0x0b},
+ {0x3f03, 0x00},
+ {0x4001, 0xe0},
+ {0x4008, 0x00},
+ {0x4009, 0x0d},
+ {0x4011, 0xf0},
+ {0x4017, 0x08},
+ {0x4050, 0x04},
+ {0x4051, 0x0b},
+ {0x4052, 0x00},
+ {0x4053, 0x80},
+ {0x4054, 0x00},
+ {0x4055, 0x80},
+ {0x4056, 0x00},
+ {0x4057, 0x80},
+ {0x4058, 0x00},
+ {0x4059, 0x80},
+ {0x405e, 0x20},
+ {0x4500, 0x07},
+ {0x4503, 0x00},
+ {0x450a, 0x04},
+ {0x4809, 0x04},
+ {0x480c, 0x12},
+ {0x481f, 0x30},
+ {0x4833, 0x10},
+ {0x4837, 0x1c},
+ {0x4902, 0x01},
+ {0x4d00, 0x03},
+ {0x4d01, 0xc9},
+ {0x4d02, 0xbc},
+ {0x4d03, 0xd7},
+ {0x4d04, 0xf0},
+ {0x4d05, 0xa2},
+ {0x5000, 0xfd},
+ {0x5001, 0x01},
+ {0x5040, 0x39},
+ {0x5041, 0x10},
+ {0x5042, 0x10},
+ {0x5043, 0x84},
+ {0x5044, 0x62},
+ {0x5180, 0x00},
+ {0x5181, 0x10},
+ {0x5182, 0x02},
+ {0x5183, 0x0f},
+ {0x5200, 0x1b},
+ {0x520b, 0x07},
+ {0x520c, 0x0f},
+ {0x5300, 0x04},
+ {0x5301, 0x0c},
+ {0x5302, 0x0c},
+ {0x5303, 0x0f},
+ {0x5304, 0x00},
+ {0x5305, 0x70},
+ {0x5306, 0x00},
+ {0x5307, 0x80},
+ {0x5308, 0x00},
+ {0x5309, 0xa5},
+ {0x530a, 0x00},
+ {0x530b, 0xd3},
+ {0x530c, 0x00},
+ {0x530d, 0xf0},
+ {0x530e, 0x01},
+ {0x530f, 0x10},
+ {0x5310, 0x01},
+ {0x5311, 0x20},
+ {0x5312, 0x01},
+ {0x5313, 0x20},
+ {0x5314, 0x01},
+ {0x5315, 0x20},
+ {0x5316, 0x08},
+ {0x5317, 0x08},
+ {0x5318, 0x10},
+ {0x5319, 0x88},
+ {0x531a, 0x88},
+ {0x531b, 0xa9},
+ {0x531c, 0xaa},
+ {0x531d, 0x0a},
+ {0x5405, 0x02},
+ {0x5406, 0x67},
+ {0x5407, 0x01},
+ {0x5408, 0x4a},
+};
+
+static const struct ov13858_reg mode_2112x1188_regs[] = {
+ {0x3013, 0x32},
+ {0x301b, 0xf0},
+ {0x301f, 0xd0},
+ {0x3106, 0x15},
+ {0x3107, 0x23},
+ {0x350a, 0x00},
+ {0x350e, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x2b},
+ {0x3601, 0x52},
+ {0x3602, 0x60},
+ {0x3612, 0x05},
+ {0x3613, 0xa4},
+ {0x3620, 0x80},
+ {0x3621, 0x10},
+ {0x3622, 0x30},
+ {0x3624, 0x1c},
+ {0x3640, 0x10},
+ {0x3641, 0x70},
+ {0x3661, 0x80},
+ {0x3662, 0x10},
+ {0x3664, 0x73},
+ {0x3665, 0xa7},
+ {0x366e, 0xff},
+ {0x366f, 0xf4},
+ {0x3674, 0x00},
+ {0x3679, 0x0c},
+ {0x367f, 0x01},
+ {0x3680, 0x0c},
+ {0x3681, 0x50},
+ {0x3682, 0x50},
+ {0x3683, 0xa9},
+ {0x3684, 0xa9},
+ {0x3709, 0x5f},
+ {0x3714, 0x28},
+ {0x371a, 0x3e},
+ {0x3737, 0x08},
+ {0x3738, 0xcc},
+ {0x3739, 0x20},
+ {0x373d, 0x26},
+ {0x3764, 0x20},
+ {0x3765, 0x20},
+ {0x37a1, 0x36},
+ {0x37a8, 0x3b},
+ {0x37ab, 0x31},
+ {0x37c2, 0x14},
+ {0x37c3, 0xf1},
+ {0x37c5, 0x00},
+ {0x37d8, 0x03},
+ {0x37d9, 0x0c},
+ {0x37da, 0xc2},
+ {0x37dc, 0x02},
+ {0x37e0, 0x00},
+ {0x37e1, 0x0a},
+ {0x37e2, 0x14},
+ {0x37e3, 0x08},
+ {0x37e4, 0x38},
+ {0x37e5, 0x03},
+ {0x37e6, 0x08},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x01},
+ {0x3803, 0x84},
+ {0x3804, 0x10},
+ {0x3805, 0x9f},
+ {0x3806, 0x0a},
+ {0x3807, 0xd3},
+ {0x3808, 0x08},
+ {0x3809, 0x40},
+ {0x380a, 0x04},
+ {0x380b, 0xa4},
+ {0x380c, 0x04},
+ {0x380d, 0x62},
+ {0x380e, 0x0c},
+ {0x380f, 0x8e},
+ {0x3811, 0x08},
+ {0x3813, 0x03},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3820, 0xab},
+ {0x3821, 0x00},
+ {0x3822, 0xc2},
+ {0x3823, 0x18},
+ {0x3826, 0x04},
+ {0x3827, 0x90},
+ {0x3829, 0x07},
+ {0x3832, 0x00},
+ {0x3c80, 0x00},
+ {0x3c87, 0x01},
+ {0x3c8c, 0x19},
+ {0x3c8d, 0x1c},
+ {0x3c90, 0x00},
+ {0x3c91, 0x00},
+ {0x3c92, 0x00},
+ {0x3c93, 0x00},
+ {0x3c94, 0x40},
+ {0x3c95, 0x54},
+ {0x3c96, 0x34},
+ {0x3c97, 0x04},
+ {0x3c98, 0x00},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xc0},
+ {0x3f00, 0x0b},
+ {0x3f03, 0x00},
+ {0x4001, 0xe0},
+ {0x4008, 0x00},
+ {0x4009, 0x0d},
+ {0x4011, 0xf0},
+ {0x4017, 0x08},
+ {0x4050, 0x04},
+ {0x4051, 0x0b},
+ {0x4052, 0x00},
+ {0x4053, 0x80},
+ {0x4054, 0x00},
+ {0x4055, 0x80},
+ {0x4056, 0x00},
+ {0x4057, 0x80},
+ {0x4058, 0x00},
+ {0x4059, 0x80},
+ {0x405e, 0x20},
+ {0x4500, 0x07},
+ {0x4503, 0x00},
+ {0x450a, 0x04},
+ {0x4809, 0x04},
+ {0x480c, 0x12},
+ {0x481f, 0x30},
+ {0x4833, 0x10},
+ {0x4837, 0x1c},
+ {0x4902, 0x01},
+ {0x4d00, 0x03},
+ {0x4d01, 0xc9},
+ {0x4d02, 0xbc},
+ {0x4d03, 0xd7},
+ {0x4d04, 0xf0},
+ {0x4d05, 0xa2},
+ {0x5000, 0xfd},
+ {0x5001, 0x01},
+ {0x5040, 0x39},
+ {0x5041, 0x10},
+ {0x5042, 0x10},
+ {0x5043, 0x84},
+ {0x5044, 0x62},
+ {0x5180, 0x00},
+ {0x5181, 0x10},
+ {0x5182, 0x02},
+ {0x5183, 0x0f},
+ {0x5200, 0x1b},
+ {0x520b, 0x07},
+ {0x520c, 0x0f},
+ {0x5300, 0x04},
+ {0x5301, 0x0c},
+ {0x5302, 0x0c},
+ {0x5303, 0x0f},
+ {0x5304, 0x00},
+ {0x5305, 0x70},
+ {0x5306, 0x00},
+ {0x5307, 0x80},
+ {0x5308, 0x00},
+ {0x5309, 0xa5},
+ {0x530a, 0x00},
+ {0x530b, 0xd3},
+ {0x530c, 0x00},
+ {0x530d, 0xf0},
+ {0x530e, 0x01},
+ {0x530f, 0x10},
+ {0x5310, 0x01},
+ {0x5311, 0x20},
+ {0x5312, 0x01},
+ {0x5313, 0x20},
+ {0x5314, 0x01},
+ {0x5315, 0x20},
+ {0x5316, 0x08},
+ {0x5317, 0x08},
+ {0x5318, 0x10},
+ {0x5319, 0x88},
+ {0x531a, 0x88},
+ {0x531b, 0xa9},
+ {0x531c, 0xaa},
+ {0x531d, 0x0a},
+ {0x5405, 0x02},
+ {0x5406, 0x67},
+ {0x5407, 0x01},
+ {0x5408, 0x4a},
+};
+
+static const struct ov13858_reg mode_1056x784_regs[] = {
+ {0x3013, 0x32},
+ {0x301b, 0xf0},
+ {0x301f, 0xd0},
+ {0x3106, 0x15},
+ {0x3107, 0x23},
+ {0x350a, 0x00},
+ {0x350e, 0x00},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3600, 0x2b},
+ {0x3601, 0x52},
+ {0x3602, 0x60},
+ {0x3612, 0x05},
+ {0x3613, 0xa4},
+ {0x3620, 0x80},
+ {0x3621, 0x10},
+ {0x3622, 0x30},
+ {0x3624, 0x1c},
+ {0x3640, 0x10},
+ {0x3641, 0x70},
+ {0x3661, 0x80},
+ {0x3662, 0x08},
+ {0x3664, 0x73},
+ {0x3665, 0xa7},
+ {0x366e, 0xff},
+ {0x366f, 0xf4},
+ {0x3674, 0x00},
+ {0x3679, 0x0c},
+ {0x367f, 0x01},
+ {0x3680, 0x0c},
+ {0x3681, 0x50},
+ {0x3682, 0x50},
+ {0x3683, 0xa9},
+ {0x3684, 0xa9},
+ {0x3709, 0x5f},
+ {0x3714, 0x30},
+ {0x371a, 0x3e},
+ {0x3737, 0x08},
+ {0x3738, 0xcc},
+ {0x3739, 0x20},
+ {0x373d, 0x26},
+ {0x3764, 0x20},
+ {0x3765, 0x20},
+ {0x37a1, 0x36},
+ {0x37a8, 0x3b},
+ {0x37ab, 0x31},
+ {0x37c2, 0x2c},
+ {0x37c3, 0xf1},
+ {0x37c5, 0x00},
+ {0x37d8, 0x03},
+ {0x37d9, 0x06},
+ {0x37da, 0xc2},
+ {0x37dc, 0x02},
+ {0x37e0, 0x00},
+ {0x37e1, 0x0a},
+ {0x37e2, 0x14},
+ {0x37e3, 0x08},
+ {0x37e4, 0x36},
+ {0x37e5, 0x03},
+ {0x37e6, 0x08},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x10},
+ {0x3805, 0x9f},
+ {0x3806, 0x0c},
+ {0x3807, 0x5f},
+ {0x3808, 0x04},
+ {0x3809, 0x20},
+ {0x380a, 0x03},
+ {0x380b, 0x10},
+ {0x380c, 0x04},
+ {0x380d, 0x62},
+ {0x380e, 0x0c},
+ {0x380f, 0x8e},
+ {0x3811, 0x04},
+ {0x3813, 0x05},
+ {0x3814, 0x07},
+ {0x3815, 0x01},
+ {0x3816, 0x07},
+ {0x3817, 0x01},
+ {0x3820, 0xac},
+ {0x3821, 0x00},
+ {0x3822, 0xc2},
+ {0x3823, 0x18},
+ {0x3826, 0x04},
+ {0x3827, 0x48},
+ {0x3829, 0x03},
+ {0x3832, 0x00},
+ {0x3c80, 0x00},
+ {0x3c87, 0x01},
+ {0x3c8c, 0x19},
+ {0x3c8d, 0x1c},
+ {0x3c90, 0x00},
+ {0x3c91, 0x00},
+ {0x3c92, 0x00},
+ {0x3c93, 0x00},
+ {0x3c94, 0x40},
+ {0x3c95, 0x54},
+ {0x3c96, 0x34},
+ {0x3c97, 0x04},
+ {0x3c98, 0x00},
+ {0x3d8c, 0x73},
+ {0x3d8d, 0xc0},
+ {0x3f00, 0x0b},
+ {0x3f03, 0x00},
+ {0x4001, 0xe0},
+ {0x4008, 0x00},
+ {0x4009, 0x05},
+ {0x4011, 0xf0},
+ {0x4017, 0x08},
+ {0x4050, 0x02},
+ {0x4051, 0x05},
+ {0x4052, 0x00},
+ {0x4053, 0x80},
+ {0x4054, 0x00},
+ {0x4055, 0x80},
+ {0x4056, 0x00},
+ {0x4057, 0x80},
+ {0x4058, 0x00},
+ {0x4059, 0x80},
+ {0x405e, 0x20},
+ {0x4500, 0x07},
+ {0x4503, 0x00},
+ {0x450a, 0x04},
+ {0x4809, 0x04},
+ {0x480c, 0x12},
+ {0x481f, 0x30},
+ {0x4833, 0x10},
+ {0x4837, 0x1e},
+ {0x4902, 0x02},
+ {0x4d00, 0x03},
+ {0x4d01, 0xc9},
+ {0x4d02, 0xbc},
+ {0x4d03, 0xd7},
+ {0x4d04, 0xf0},
+ {0x4d05, 0xa2},
+ {0x5000, 0xfd},
+ {0x5001, 0x01},
+ {0x5040, 0x39},
+ {0x5041, 0x10},
+ {0x5042, 0x10},
+ {0x5043, 0x84},
+ {0x5044, 0x62},
+ {0x5180, 0x00},
+ {0x5181, 0x10},
+ {0x5182, 0x02},
+ {0x5183, 0x0f},
+ {0x5200, 0x1b},
+ {0x520b, 0x07},
+ {0x520c, 0x0f},
+ {0x5300, 0x04},
+ {0x5301, 0x0c},
+ {0x5302, 0x0c},
+ {0x5303, 0x0f},
+ {0x5304, 0x00},
+ {0x5305, 0x70},
+ {0x5306, 0x00},
+ {0x5307, 0x80},
+ {0x5308, 0x00},
+ {0x5309, 0xa5},
+ {0x530a, 0x00},
+ {0x530b, 0xd3},
+ {0x530c, 0x00},
+ {0x530d, 0xf0},
+ {0x530e, 0x01},
+ {0x530f, 0x10},
+ {0x5310, 0x01},
+ {0x5311, 0x20},
+ {0x5312, 0x01},
+ {0x5313, 0x20},
+ {0x5314, 0x01},
+ {0x5315, 0x20},
+ {0x5316, 0x08},
+ {0x5317, 0x08},
+ {0x5318, 0x10},
+ {0x5319, 0x88},
+ {0x531a, 0x88},
+ {0x531b, 0xa9},
+ {0x531c, 0xaa},
+ {0x531d, 0x0a},
+ {0x5405, 0x02},
+ {0x5406, 0x67},
+ {0x5407, 0x01},
+ {0x5408, 0x4a},
+};
+
+static const char * const ov13858_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* Configurations for supported link frequencies */
+#define OV13858_NUM_OF_LINK_FREQS 2
+#define OV13858_LINK_FREQ_1080MBPS 1080000000
+#define OV13858_LINK_FREQ_540MBPS 540000000
+#define OV13858_LINK_FREQ_INDEX_0 0
+#define OV13858_LINK_FREQ_INDEX_1 1
+
+/* Menu items for LINK_FREQ V4L2 control */
+static const s64 link_freq_menu_items[OV13858_NUM_OF_LINK_FREQS] = {
+ OV13858_LINK_FREQ_1080MBPS,
+ OV13858_LINK_FREQ_540MBPS
+};
+
+/* Link frequency configs */
+static const struct ov13858_link_freq_config
+ link_freq_configs[OV13858_NUM_OF_LINK_FREQS] = {
+ {
+ .pixel_rate = 864000000,
+ .pixels_per_line = OV13858_PPL_1080MHZ,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_1080mbps),
+ .regs = mipi_data_rate_1080mbps,
+ }
+ },
+ {
+ .pixel_rate = 432000000,
+ .pixels_per_line = OV13858_PPL_540MHZ,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_540mbps),
+ .regs = mipi_data_rate_540mbps,
+ }
+ }
+};
+
+/* Mode configs */
+static const struct ov13858_mode supported_modes[] = {
+ {
+ .width = 4224,
+ .height = 3136,
+ .vts = OV13858_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_4224x3136_regs),
+ .regs = mode_4224x3136_regs,
+ },
+ .link_freq_index = OV13858_LINK_FREQ_INDEX_0,
+ },
+ {
+ .width = 2112,
+ .height = 1568,
+ .vts = OV13858_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2112x1568_regs),
+ .regs = mode_2112x1568_regs,
+ },
+ .link_freq_index = OV13858_LINK_FREQ_INDEX_1,
+ },
+ {
+ .width = 2112,
+ .height = 1188,
+ .vts = OV13858_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2112x1188_regs),
+ .regs = mode_2112x1188_regs,
+ },
+ .link_freq_index = OV13858_LINK_FREQ_INDEX_1,
+ },
+ {
+ .width = 1056,
+ .height = 784,
+ .vts = OV13858_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1056x784_regs),
+ .regs = mode_1056x784_regs,
+ },
+ .link_freq_index = OV13858_LINK_FREQ_INDEX_1,
+ }
+};
+
+struct ov13858 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* V4L2 Controls */
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *exposure;
+
+ /* Current mode */
+ const struct ov13858_mode *cur_mode;
+
+ /* Mutex for serialized access */
+ struct mutex mutex;
+
+ /* Streaming on/off */
+ bool streaming;
+};
+
+#define to_ov13858(_sd) container_of(_sd, struct ov13858, sd)
+
+/* Read registers up to 4 at a time */
+static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ int ret;
+ u32 data_be = 0;
+ u16 reg_addr_be = cpu_to_be16(reg);
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+/* Write registers up to 4 at a time */
+static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ int buf_i, val_i;
+ u8 buf[6], *val_p;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val = cpu_to_be32(val);
+ val_p = (u8 *)&val;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/* Write a list of registers */
+static int ov13858_write_regs(struct ov13858 *ov13858,
+ const struct ov13858_reg *regs, u32 len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ int ret;
+ u32 i;
+
+ for (i = 0; i < len; i++) {
+ ret = ov13858_write_reg(ov13858, regs[i].address, 1,
+ regs[i].val);
+ if (ret) {
+ dev_err_ratelimited(
+ &client->dev,
+ "Failed to write reg 0x%4.4x. error = %d\n",
+ regs[i].address, ret);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ov13858_write_reg_list(struct ov13858 *ov13858,
+ const struct ov13858_reg_list *r_list)
+{
+ return ov13858_write_regs(ov13858, r_list->regs, r_list->num_of_regs);
+}
+
+/* Open sub-device */
+static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov13858 *ov13858 = to_ov13858(sd);
+ struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd,
+ fh->pad,
+ 0);
+
+ mutex_lock(&ov13858->mutex);
+
+ /* Initialize try_fmt */
+ try_fmt->width = ov13858->cur_mode->width;
+ try_fmt->height = ov13858->cur_mode->height;
+ try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ /* No crop or compose */
+ mutex_unlock(&ov13858->mutex);
+
+ return 0;
+}
+
+static int ov13858_update_digital_gain(struct ov13858 *ov13858, u32 d_gain)
+{
+ int ret;
+ u32 val;
+
+ if (d_gain == 3)
+ return -EINVAL;
+
+ ret = ov13858_read_reg(ov13858, OV13858_REG_DIGITAL_GAIN,
+ OV13858_REG_VALUE_08BIT, &val);
+ if (ret)
+ return ret;
+
+ val &= OV13858_DGTL_GAIN_MASK;
+ val |= (d_gain - 1) << OV13858_DGTL_GAIN_SHIFT;
+
+ return ov13858_write_reg(ov13858, OV13858_REG_DIGITAL_GAIN,
+ OV13858_REG_VALUE_08BIT, val);
+}
+
+static int ov13858_enable_test_pattern(struct ov13858 *ov13858, u32 pattern)
+{
+ int ret;
+ u32 val;
+
+ ret = ov13858_read_reg(ov13858, OV13858_REG_TEST_PATTERN,
+ OV13858_REG_VALUE_08BIT, &val);
+ if (ret)
+ return ret;
+
+ if (pattern) {
+ val &= OV13858_TEST_PATTERN_MASK;
+ val |= (pattern - 1) | OV13858_TEST_PATTERN_ENABLE;
+ } else {
+ val &= ~OV13858_TEST_PATTERN_ENABLE;
+ }
+
+ return ov13858_write_reg(ov13858, OV13858_REG_TEST_PATTERN,
+ OV13858_REG_VALUE_08BIT, val);
+}
+
+static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov13858 *ov13858 = container_of(ctrl->handler,
+ struct ov13858, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ s64 max;
+ int ret;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov13858->cur_mode->height + ctrl->val - 8;
+ __v4l2_ctrl_modify_range(ov13858->exposure,
+ ov13858->exposure->minimum,
+ max, ov13858->exposure->step, max);
+ break;
+ };
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is up for streaming
+ */
+ if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ return 0;
+
+ ret = 0;
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov13858_write_reg(ov13858, OV13858_REG_ANALOG_GAIN,
+ OV13858_REG_VALUE_16BIT, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ov13858_update_digital_gain(ov13858, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = ov13858_write_reg(ov13858, OV13858_REG_EXPOSURE,
+ OV13858_REG_VALUE_24BIT,
+ ctrl->val << 4);
+ break;
+ case V4L2_CID_VBLANK:
+ /* Update VTS that meets expected vertical blanking */
+ ret = ov13858_write_reg(ov13858, OV13858_REG_VTS,
+ OV13858_REG_VALUE_16BIT,
+ ov13858->cur_mode->height
+ + ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov13858_enable_test_pattern(ov13858, ctrl->val);
+ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+ ctrl->id, ctrl->val);
+ break;
+ };
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov13858_ctrl_ops = {
+ .s_ctrl = ov13858_set_ctrl,
+};
+
+static int ov13858_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* Only one bayer order(GRBG) is supported */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ return 0;
+}
+
+static int ov13858_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ 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 void ov13858_update_pad_format(const struct ov13858_mode *mode,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->format.field = V4L2_FIELD_NONE;
+}
+
+static int ov13858_do_get_pad_format(struct ov13858 *ov13858,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *framefmt;
+ struct v4l2_subdev *sd = &ov13858->sd;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ fmt->format = *framefmt;
+ } else {
+ ov13858_update_pad_format(ov13858->cur_mode, fmt);
+ }
+
+ return 0;
+}
+
+static int ov13858_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov13858 *ov13858 = to_ov13858(sd);
+ int ret;
+
+ mutex_lock(&ov13858->mutex);
+ ret = ov13858_do_get_pad_format(ov13858, cfg, fmt);
+ mutex_unlock(&ov13858->mutex);
+
+ return ret;
+}
+
+/*
+ * Calculate resolution distance
+ */
+static int
+ov13858_get_resolution_dist(const struct ov13858_mode *mode,
+ struct v4l2_mbus_framefmt *framefmt)
+{
+ return abs(mode->width - framefmt->width) +
+ abs(mode->height - framefmt->height);
+}
+
+/*
+ * Find the closest supported resolution to the requested resolution
+ */
+static const struct ov13858_mode *
+ov13858_find_best_fit(struct ov13858 *ov13858,
+ struct v4l2_subdev_format *fmt)
+{
+ int i, dist, cur_best_fit = 0, cur_best_fit_dist = -1;
+ struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+
+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+ dist = ov13858_get_resolution_dist(&supported_modes[i],
+ framefmt);
+ if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+ cur_best_fit_dist = dist;
+ cur_best_fit = i;
+ }
+ }
+
+ return &supported_modes[cur_best_fit];
+}
+
+static int
+ov13858_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov13858 *ov13858 = to_ov13858(sd);
+ const struct ov13858_mode *mode;
+ struct v4l2_mbus_framefmt *framefmt;
+ s64 h_blank;
+
+ mutex_lock(&ov13858->mutex);
+
+ /* Only one raw bayer(GRBG) order is supported */
+ if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ mode = ov13858_find_best_fit(ov13858, fmt);
+ ov13858_update_pad_format(mode, fmt);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ *framefmt = fmt->format;
+ } else {
+ ov13858->cur_mode = mode;
+ __v4l2_ctrl_s_ctrl(ov13858->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(
+ ov13858->pixel_rate,
+ link_freq_configs[mode->link_freq_index].pixel_rate);
+ /* Update limits and set FPS to default */
+ __v4l2_ctrl_modify_range(
+ ov13858->vblank, OV13858_VBLANK_MIN,
+ OV13858_VTS_MAX - ov13858->cur_mode->height, 1,
+ ov13858->cur_mode->vts - ov13858->cur_mode->height);
+ h_blank =
+ link_freq_configs[mode->link_freq_index].pixels_per_line
+ - ov13858->cur_mode->width;
+ __v4l2_ctrl_modify_range(ov13858->hblank, h_blank,
+ h_blank, 1, h_blank);
+ }
+
+ mutex_unlock(&ov13858->mutex);
+
+ return 0;
+}
+
+static int ov13858_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
+{
+ *frames = OV13858_NUM_OF_SKIP_FRAMES;
+
+ return 0;
+}
+
+/* Start streaming */
+static int ov13858_start_streaming(struct ov13858 *ov13858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ const struct ov13858_reg_list *reg_list;
+ int ret, link_freq_index;
+
+ /* Get out of from software reset */
+ ret = ov13858_write_reg(ov13858, OV13858_REG_SOFTWARE_RST,
+ OV13858_REG_VALUE_08BIT, OV13858_SOFTWARE_RST);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set powerup registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Setup PLL */
+ link_freq_index = ov13858->cur_mode->link_freq_index;
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = ov13858_write_reg_list(ov13858, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set plls\n", __func__);
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ reg_list = &ov13858->cur_mode->reg_list;
+ ret = ov13858_write_reg_list(ov13858, reg_list);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set mode\n", __func__);
+ return ret;
+ }
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(ov13858->sd.ctrl_handler);
+ if (ret)
+ return ret;
+
+ return ov13858_write_reg(ov13858, OV13858_REG_MODE_SELECT,
+ OV13858_REG_VALUE_08BIT,
+ OV13858_MODE_STREAMING);
+}
+
+/* Stop streaming */
+static int ov13858_stop_streaming(struct ov13858 *ov13858)
+{
+ return ov13858_write_reg(ov13858, OV13858_REG_MODE_SELECT,
+ OV13858_REG_VALUE_08BIT, OV13858_MODE_STANDBY);
+}
+
+static int ov13858_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov13858 *ov13858 = to_ov13858(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&ov13858->mutex);
+ if (ov13858->streaming == enable) {
+ mutex_unlock(&ov13858->mutex);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto err_unlock;
+ }
+
+ /*
+ * Apply default & customized values
+ * and then start streaming.
+ */
+ ret = ov13858_start_streaming(ov13858);
+ if (ret)
+ goto err_rpm_put;
+ } else {
+ ov13858_stop_streaming(ov13858);
+ pm_runtime_put(&client->dev);
+ }
+
+ ov13858->streaming = enable;
+ mutex_unlock(&ov13858->mutex);
+
+ return ret;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+err_unlock:
+ mutex_unlock(&ov13858->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused ov13858_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov13858 *ov13858 = to_ov13858(sd);
+
+ if (ov13858->streaming)
+ ov13858_stop_streaming(ov13858);
+
+ return 0;
+}
+
+static int __maybe_unused ov13858_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov13858 *ov13858 = to_ov13858(sd);
+ int ret;
+
+ if (ov13858->streaming) {
+ ret = ov13858_start_streaming(ov13858);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ ov13858_stop_streaming(ov13858);
+ ov13858->streaming = 0;
+ return ret;
+}
+
+/* Verify chip ID */
+static int ov13858_identify_module(struct ov13858 *ov13858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ int ret;
+ u32 val;
+
+ ret = ov13858_read_reg(ov13858, OV13858_REG_CHIP_ID,
+ OV13858_REG_VALUE_24BIT, &val);
+ if (ret)
+ return ret;
+
+ if (val != OV13858_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+ OV13858_CHIP_ID, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov13858_video_ops = {
+ .s_stream = ov13858_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov13858_pad_ops = {
+ .enum_mbus_code = ov13858_enum_mbus_code,
+ .get_fmt = ov13858_get_pad_format,
+ .set_fmt = ov13858_set_pad_format,
+ .enum_frame_size = ov13858_enum_frame_size,
+};
+
+static const struct v4l2_subdev_sensor_ops ov13858_sensor_ops = {
+ .g_skip_frames = ov13858_get_skip_frames,
+};
+
+static const struct v4l2_subdev_ops ov13858_subdev_ops = {
+ .video = &ov13858_video_ops,
+ .pad = &ov13858_pad_ops,
+ .sensor = &ov13858_sensor_ops,
+};
+
+static const struct media_entity_operations ov13858_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov13858_internal_ops = {
+ .open = ov13858_open,
+};
+
+/* Initialize control handlers */
+static int ov13858_init_controls(struct ov13858 *ov13858)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ int ret;
+
+ ctrl_hdlr = &ov13858->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ if (ret)
+ return ret;
+
+ mutex_init(&ov13858->mutex);
+ ctrl_hdlr->lock = &ov13858->mutex;
+ ov13858->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &ov13858_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ OV13858_NUM_OF_LINK_FREQS - 1,
+ 0,
+ link_freq_menu_items);
+ ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* By default, PIXEL_RATE is read only */
+ ov13858->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov13858_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ link_freq_configs[0].pixel_rate, 1,
+ link_freq_configs[0].pixel_rate);
+
+ ov13858->vblank = v4l2_ctrl_new_std(
+ ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_VBLANK,
+ OV13858_VBLANK_MIN,
+ OV13858_VTS_MAX - ov13858->cur_mode->height, 1,
+ ov13858->cur_mode->vts
+ - ov13858->cur_mode->height);
+
+ ov13858->hblank = v4l2_ctrl_new_std(
+ ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_HBLANK,
+ OV13858_PPL_1080MHZ - ov13858->cur_mode->width,
+ OV13858_PPL_1080MHZ - ov13858->cur_mode->width,
+ 1,
+ OV13858_PPL_1080MHZ - ov13858->cur_mode->width);
+ ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov13858->exposure = v4l2_ctrl_new_std(
+ ctrl_hdlr, &ov13858_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV13858_EXPOSURE_MIN,
+ OV13858_EXPOSURE_MAX, OV13858_EXPOSURE_STEP,
+ OV13858_EXPOSURE_DEFAULT);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ OV13858_ANA_GAIN_MIN, OV13858_ANA_GAIN_MAX,
+ OV13858_ANA_GAIN_STEP, OV13858_ANA_GAIN_DEFAULT);
+
+ /* Digital gain */
+ v4l2_ctrl_new_std(ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ OV13858_DGTL_GAIN_MIN, OV13858_DGTL_GAIN_MAX,
+ OV13858_DGTL_GAIN_STEP, OV13858_DGTL_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov13858_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov13858_test_pattern_menu) - 1,
+ 0, 0, ov13858_test_pattern_menu);
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ dev_err(&client->dev, "%s control init failed (%d)\n",
+ __func__, ret);
+ goto error;
+ }
+
+ ov13858->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ mutex_destroy(&ov13858->mutex);
+
+ return ret;
+}
+
+static void ov13858_free_controls(struct ov13858 *ov13858)
+{
+ v4l2_ctrl_handler_free(ov13858->sd.ctrl_handler);
+ mutex_destroy(&ov13858->mutex);
+}
+
+static int ov13858_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ov13858 *ov13858;
+ int ret;
+ u32 val = 0;
+
+ device_property_read_u32(&client->dev, "clock-frequency", &val);
+ if (val != 19200000)
+ return -EINVAL;
+
+ ov13858 = devm_kzalloc(&client->dev, sizeof(*ov13858), GFP_KERNEL);
+ if (!ov13858)
+ return -ENOMEM;
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops);
+
+ /* Check module identity */
+ ret = ov13858_identify_module(ov13858);
+ if (ret) {
+ dev_err(&client->dev, "failed to find sensor: %d\n", ret);
+ return ret;
+ }
+
+ /* Set default mode to max resolution */
+ ov13858->cur_mode = &supported_modes[0];
+
+ ret = ov13858_init_controls(ov13858);
+ if (ret)
+ return ret;
+
+ /* Initialize subdev */
+ ov13858->sd.internal_ops = &ov13858_internal_ops;
+ ov13858->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov13858->sd.entity.ops = &ov13858_subdev_entity_ops;
+ ov13858->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ ov13858->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&ov13858->sd.entity, 1, &ov13858->pad);
+ if (ret) {
+ dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_async_register_subdev(&ov13858->sd);
+ if (ret < 0)
+ goto error_media_entity;
+
+ /*
+ * Device is already turned on by i2c-core with ACPI domain PM.
+ * Enable runtime PM and turn off the device.
+ */
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_put(&client->dev);
+
+ return 0;
+
+error_media_entity:
+ media_entity_cleanup(&ov13858->sd.entity);
+
+error_handler_free:
+ ov13858_free_controls(ov13858);
+ dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int ov13858_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov13858 *ov13858 = to_ov13858(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ ov13858_free_controls(ov13858);
+
+ /*
+ * Disable runtime PM but keep the device turned on.
+ * i2c-core with ACPI domain PM will turn off the device.
+ */
+ pm_runtime_get_sync(&client->dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov13858_id_table[] = {
+ {"ov13858", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov13858_id_table);
+
+static const struct dev_pm_ops ov13858_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ov13858_suspend, ov13858_resume)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ov13858_acpi_ids[] = {
+ {"OVTID858"},
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids);
+#endif
+
+static struct i2c_driver ov13858_i2c_driver = {
+ .driver = {
+ .name = "ov13858",
+ .owner = THIS_MODULE,
+ .pm = &ov13858_pm_ops,
+ .acpi_match_table = ACPI_PTR(ov13858_acpi_ids),
+ },
+ .probe = ov13858_probe,
+ .remove = ov13858_remove,
+ .id_table = ov13858_id_table,
+};
+
+module_i2c_driver(ov13858_i2c_driver);
+
+MODULE_AUTHOR("Kan, Chris <chris.kan@intel.com>");
+MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>");
+MODULE_AUTHOR("Yang, Hyungwoo <hyungwoo.yang@intel.com>");
+MODULE_DESCRIPTION("Omnivision ov13858 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 6e6367214d40..122dd6c5eb38 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -42,9 +42,9 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-mediabus.h>
-#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
#define DRIVER_NAME "ov2659"
@@ -1308,7 +1308,8 @@ static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = {
static int ov2659_detect(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 pid, ver;
+ u8 pid = 0;
+ u8 ver = 0;
int ret;
dev_dbg(&client->dev, "%s:\n", __func__);
@@ -1346,7 +1347,7 @@ static struct ov2659_platform_data *
ov2659_get_pdata(struct i2c_client *client)
{
struct ov2659_platform_data *pdata;
- struct v4l2_of_endpoint *bus_cfg;
+ struct v4l2_fwnode_endpoint *bus_cfg;
struct device_node *endpoint;
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
@@ -1356,7 +1357,7 @@ ov2659_get_pdata(struct i2c_client *client)
if (!endpoint)
return NULL;
- bus_cfg = v4l2_of_alloc_parse_endpoint(endpoint);
+ bus_cfg = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(endpoint));
if (IS_ERR(bus_cfg)) {
pdata = NULL;
goto done;
@@ -1376,7 +1377,7 @@ ov2659_get_pdata(struct i2c_client *client)
pdata->link_frequency = bus_cfg->link_frequencies[0];
done:
- v4l2_of_free_endpoint(bus_cfg);
+ v4l2_fwnode_endpoint_free(bus_cfg);
of_node_put(endpoint);
return pdata;
}
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
new file mode 100644
index 000000000000..1f5b483cf334
--- /dev/null
+++ b/drivers/media/i2c/ov5640.c
@@ -0,0 +1,2344 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* min/typical/max system clock (xclk) frequencies */
+#define OV5640_XCLK_MIN 6000000
+#define OV5640_XCLK_MAX 24000000
+
+#define OV5640_DEFAULT_SLAVE_ID 0x3c
+
+#define OV5640_REG_CHIP_ID 0x300a
+#define OV5640_REG_PAD_OUTPUT00 0x3019
+#define OV5640_REG_SC_PLL_CTRL0 0x3034
+#define OV5640_REG_SC_PLL_CTRL1 0x3035
+#define OV5640_REG_SC_PLL_CTRL2 0x3036
+#define OV5640_REG_SC_PLL_CTRL3 0x3037
+#define OV5640_REG_SLAVE_ID 0x3100
+#define OV5640_REG_SYS_ROOT_DIVIDER 0x3108
+#define OV5640_REG_AWB_R_GAIN 0x3400
+#define OV5640_REG_AWB_G_GAIN 0x3402
+#define OV5640_REG_AWB_B_GAIN 0x3404
+#define OV5640_REG_AWB_MANUAL_CTRL 0x3406
+#define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500
+#define OV5640_REG_AEC_PK_EXPOSURE_MED 0x3501
+#define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502
+#define OV5640_REG_AEC_PK_MANUAL 0x3503
+#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
+#define OV5640_REG_AEC_PK_VTS 0x350c
+#define OV5640_REG_TIMING_HTS 0x380c
+#define OV5640_REG_TIMING_VTS 0x380e
+#define OV5640_REG_TIMING_TC_REG21 0x3821
+#define OV5640_REG_AEC_CTRL00 0x3a00
+#define OV5640_REG_AEC_B50_STEP 0x3a08
+#define OV5640_REG_AEC_B60_STEP 0x3a0a
+#define OV5640_REG_AEC_CTRL0D 0x3a0d
+#define OV5640_REG_AEC_CTRL0E 0x3a0e
+#define OV5640_REG_AEC_CTRL0F 0x3a0f
+#define OV5640_REG_AEC_CTRL10 0x3a10
+#define OV5640_REG_AEC_CTRL11 0x3a11
+#define OV5640_REG_AEC_CTRL1B 0x3a1b
+#define OV5640_REG_AEC_CTRL1E 0x3a1e
+#define OV5640_REG_AEC_CTRL1F 0x3a1f
+#define OV5640_REG_HZ5060_CTRL00 0x3c00
+#define OV5640_REG_HZ5060_CTRL01 0x3c01
+#define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c
+#define OV5640_REG_FRAME_CTRL01 0x4202
+#define OV5640_REG_MIPI_CTRL00 0x4800
+#define OV5640_REG_DEBUG_MODE 0x4814
+#define OV5640_REG_PRE_ISP_TEST_SET1 0x503d
+#define OV5640_REG_SDE_CTRL0 0x5580
+#define OV5640_REG_SDE_CTRL1 0x5581
+#define OV5640_REG_SDE_CTRL3 0x5583
+#define OV5640_REG_SDE_CTRL4 0x5584
+#define OV5640_REG_SDE_CTRL5 0x5585
+#define OV5640_REG_AVG_READOUT 0x56a1
+
+enum ov5640_mode_id {
+ OV5640_MODE_QCIF_176_144 = 0,
+ OV5640_MODE_QVGA_320_240,
+ OV5640_MODE_VGA_640_480,
+ OV5640_MODE_NTSC_720_480,
+ OV5640_MODE_PAL_720_576,
+ OV5640_MODE_XGA_1024_768,
+ OV5640_MODE_720P_1280_720,
+ OV5640_MODE_1080P_1920_1080,
+ OV5640_MODE_QSXGA_2592_1944,
+ OV5640_NUM_MODES,
+};
+
+enum ov5640_frame_rate {
+ OV5640_15_FPS = 0,
+ OV5640_30_FPS,
+ OV5640_NUM_FRAMERATES,
+};
+
+/*
+ * FIXME: remove this when a subdev API becomes available
+ * to set the MIPI CSI-2 virtual channel.
+ */
+static unsigned int virtual_channel;
+module_param(virtual_channel, int, 0);
+MODULE_PARM_DESC(virtual_channel,
+ "MIPI CSI-2 virtual channel (0..3), default 0");
+
+static const int ov5640_framerates[] = {
+ [OV5640_15_FPS] = 15,
+ [OV5640_30_FPS] = 30,
+};
+
+/* regulator supplies */
+static const char * const ov5640_supply_name[] = {
+ "DOVDD", /* Digital I/O (1.8V) suppply */
+ "DVDD", /* Digital Core (1.5V) supply */
+ "AVDD", /* Analog (2.8V) supply */
+};
+
+#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
+
+/*
+ * Image size under 1280 * 960 are SUBSAMPLING
+ * Image size upper 1280 * 960 are SCALING
+ */
+enum ov5640_downsize_mode {
+ SUBSAMPLING,
+ SCALING,
+};
+
+struct reg_value {
+ u16 reg_addr;
+ u8 val;
+ u8 mask;
+ u32 delay_ms;
+};
+
+struct ov5640_mode_info {
+ enum ov5640_mode_id id;
+ enum ov5640_downsize_mode dn_mode;
+ u32 width;
+ u32 height;
+ const struct reg_value *reg_data;
+ u32 reg_data_size;
+};
+
+struct ov5640_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct {
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ struct v4l2_ctrl *auto_wb;
+ struct v4l2_ctrl *blue_balance;
+ struct v4l2_ctrl *red_balance;
+ };
+ struct {
+ struct v4l2_ctrl *auto_gain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *hue;
+ struct v4l2_ctrl *test_pattern;
+};
+
+struct ov5640_dev {
+ struct i2c_client *i2c_client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
+ struct clk *xclk; /* system clock to OV5640 */
+ u32 xclk_freq;
+
+ struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pwdn_gpio;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ int power_count;
+
+ struct v4l2_mbus_framefmt fmt;
+
+ const struct ov5640_mode_info *current_mode;
+ enum ov5640_frame_rate current_fr;
+ struct v4l2_fract frame_interval;
+
+ struct ov5640_ctrls ctrls;
+
+ u32 prev_sysclk, prev_hts;
+ u32 ae_low, ae_high, ae_target;
+
+ bool pending_mode_change;
+ bool streaming;
+};
+
+static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov5640_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ov5640_dev,
+ ctrls.handler)->sd;
+}
+
+/*
+ * FIXME: all of these register tables are likely filled with
+ * entries that set the register to their power-on default values,
+ * and which are otherwise not touched by this driver. Those entries
+ * should be identified and removed to speed register load time
+ * over i2c.
+ */
+
+static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
+
+ {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+ {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
+ {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
+ {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
+ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
+ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
+ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
+ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
+ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
+ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
+ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+ {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
+ {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+ {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+ {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+ {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
+ {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
+ {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
+ {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+ {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+ {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+ {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+ {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
+ {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
+ {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+ {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+ {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+ {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+ {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+ {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+ {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+ {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+ {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+ {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+ {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+ {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+ {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+ {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+ {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
+ {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
+ {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+ {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+ {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
+ {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+ {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+ {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
+ {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
+ {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
+ {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
+ {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+ {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
+ {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
+ {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
+ {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
+ {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
+ {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
+ {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
+ {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
+ {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
+ {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
+ {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
+ {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
+};
+
+static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+
+ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
+
+ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+ {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+ {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0},
+ {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+ {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+ {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
+ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+ {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+ {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
+ {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
+ {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+ {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+ {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+ {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
+ {0x3503, 0, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+ {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+ {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+ {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
+};
+
+/* power-on sensor init reg table */
+static const struct ov5640_mode_info ov5640_mode_init_data = {
+ 0, SUBSAMPLING, 640, 480, ov5640_init_setting_30fps_VGA,
+ ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
+};
+
+static const struct ov5640_mode_info
+ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
+ {
+ {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144,
+ ov5640_setting_15fps_QCIF_176_144,
+ ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
+ {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240,
+ ov5640_setting_15fps_QVGA_320_240,
+ ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
+ {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480,
+ ov5640_setting_15fps_VGA_640_480,
+ ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
+ {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480,
+ ov5640_setting_15fps_NTSC_720_480,
+ ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
+ {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576,
+ ov5640_setting_15fps_PAL_720_576,
+ ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
+ {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768,
+ ov5640_setting_15fps_XGA_1024_768,
+ ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
+ {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720,
+ ov5640_setting_15fps_720P_1280_720,
+ ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
+ {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080,
+ ov5640_setting_15fps_1080P_1920_1080,
+ ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
+ {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 1944,
+ ov5640_setting_15fps_QSXGA_2592_1944,
+ ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+ }, {
+ {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144,
+ ov5640_setting_30fps_QCIF_176_144,
+ ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
+ {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240,
+ ov5640_setting_30fps_QVGA_320_240,
+ ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
+ {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480,
+ ov5640_setting_30fps_VGA_640_480,
+ ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+ {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480,
+ ov5640_setting_30fps_NTSC_720_480,
+ ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+ {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576,
+ ov5640_setting_30fps_PAL_720_576,
+ ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
+ {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768,
+ ov5640_setting_30fps_XGA_1024_768,
+ ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
+ {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720,
+ ov5640_setting_30fps_720P_1280_720,
+ ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+ {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080,
+ ov5640_setting_30fps_1080P_1920_1080,
+ ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
+ {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+ },
+};
+
+static int ov5640_init_slave_id(struct ov5640_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct i2c_msg msg;
+ u8 buf[3];
+ int ret;
+
+ if (client->addr == OV5640_DEFAULT_SLAVE_ID)
+ return 0;
+
+ buf[0] = OV5640_REG_SLAVE_ID >> 8;
+ buf[1] = OV5640_REG_SLAVE_ID & 0xff;
+ buf[2] = client->addr << 1;
+
+ msg.addr = OV5640_DEFAULT_SLAVE_ID;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = sizeof(buf);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct i2c_msg msg;
+ u8 buf[3];
+ int ret;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = val;
+
+ msg.addr = client->addr;
+ msg.flags = client->flags;
+ msg.buf = buf;
+ msg.len = sizeof(buf);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n",
+ __func__, reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags;
+ msg[0].buf = buf;
+ msg[0].len = sizeof(buf);
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = 1;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = buf[0];
+ return 0;
+}
+
+static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
+{
+ u8 hi, lo;
+ int ret;
+
+ ret = ov5640_read_reg(sensor, reg, &hi);
+ if (ret)
+ return ret;
+ ret = ov5640_read_reg(sensor, reg+1, &lo);
+ if (ret)
+ return ret;
+
+ *val = ((u16)hi << 8) | (u16)lo;
+ return 0;
+}
+
+static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
+{
+ int ret;
+
+ ret = ov5640_write_reg(sensor, reg, val >> 8);
+ if (ret)
+ return ret;
+
+ return ov5640_write_reg(sensor, reg + 1, val & 0xff);
+}
+
+static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
+ u8 mask, u8 val)
+{
+ u8 readval;
+ int ret;
+
+ ret = ov5640_read_reg(sensor, reg, &readval);
+ if (ret)
+ return ret;
+
+ readval &= ~mask;
+ val &= mask;
+ val |= readval;
+
+ return ov5640_write_reg(sensor, reg, val);
+}
+
+/* download ov5640 settings to sensor through i2c */
+static int ov5640_load_regs(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
+{
+ const struct reg_value *regs = mode->reg_data;
+ unsigned int i;
+ u32 delay_ms;
+ u16 reg_addr;
+ u8 mask, val;
+ int ret = 0;
+
+ for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
+ delay_ms = regs->delay_ms;
+ reg_addr = regs->reg_addr;
+ val = regs->val;
+ mask = regs->mask;
+
+ if (mask)
+ ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
+ else
+ ret = ov5640_write_reg(sensor, reg_addr, val);
+ if (ret)
+ break;
+
+ if (delay_ms)
+ usleep_range(1000*delay_ms, 1000*delay_ms+100);
+ }
+
+ return ret;
+}
+
+/* read exposure, in number of line periods */
+static int ov5640_get_exposure(struct ov5640_dev *sensor)
+{
+ int exp, ret;
+ u8 temp;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
+ if (ret)
+ return ret;
+ exp = ((int)temp & 0x0f) << 16;
+ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
+ if (ret)
+ return ret;
+ exp |= ((int)temp << 8);
+ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
+ if (ret)
+ return ret;
+ exp |= (int)temp;
+
+ return exp >> 4;
+}
+
+/* write exposure, given number of line periods */
+static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
+{
+ int ret;
+
+ exposure <<= 4;
+
+ ret = ov5640_write_reg(sensor,
+ OV5640_REG_AEC_PK_EXPOSURE_LO,
+ exposure & 0xff);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor,
+ OV5640_REG_AEC_PK_EXPOSURE_MED,
+ (exposure >> 8) & 0xff);
+ if (ret)
+ return ret;
+ return ov5640_write_reg(sensor,
+ OV5640_REG_AEC_PK_EXPOSURE_HI,
+ (exposure >> 16) & 0x0f);
+}
+
+static int ov5640_get_gain(struct ov5640_dev *sensor)
+{
+ u16 gain;
+ int ret;
+
+ ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
+ if (ret)
+ return ret;
+
+ return gain & 0x3ff;
+}
+
+static int ov5640_set_stream(struct ov5640_dev *sensor, bool on)
+{
+ int ret;
+
+ ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
+ on ? 0 : BIT(5));
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
+ on ? 0x00 : 0x70);
+ if (ret)
+ return ret;
+
+ return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
+ on ? 0x00 : 0x0f);
+}
+
+static int ov5640_get_sysclk(struct ov5640_dev *sensor)
+{
+ /* calculate sysclk */
+ u32 xvclk = sensor->xclk_freq / 10000;
+ u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
+ u32 sclk_rdiv_map[] = {1, 2, 4, 8};
+ u32 bit_div2x = 1, sclk_rdiv, sysclk;
+ u8 temp1, temp2;
+ int ret;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
+ if (ret)
+ return ret;
+ temp2 = temp1 & 0x0f;
+ if (temp2 == 8 || temp2 == 10)
+ bit_div2x = temp2 / 2;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
+ if (ret)
+ return ret;
+ sysdiv = temp1 >> 4;
+ if (sysdiv == 0)
+ sysdiv = 16;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
+ if (ret)
+ return ret;
+ multiplier = temp1;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
+ if (ret)
+ return ret;
+ prediv = temp1 & 0x0f;
+ pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
+ if (ret)
+ return ret;
+ temp2 = temp1 & 0x03;
+ sclk_rdiv = sclk_rdiv_map[temp2];
+
+ if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
+ return -EINVAL;
+
+ VCO = xvclk * multiplier / prediv;
+
+ sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
+
+ return sysclk;
+}
+
+static int ov5640_set_night_mode(struct ov5640_dev *sensor)
+{
+ /* read HTS from register settings */
+ u8 mode;
+ int ret;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
+ if (ret)
+ return ret;
+ mode &= 0xfb;
+ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
+}
+
+static int ov5640_get_hts(struct ov5640_dev *sensor)
+{
+ /* read HTS from register settings */
+ u16 hts;
+ int ret;
+
+ ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
+ if (ret)
+ return ret;
+ return hts;
+}
+
+static int ov5640_get_vts(struct ov5640_dev *sensor)
+{
+ u16 vts;
+ int ret;
+
+ ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
+ if (ret)
+ return ret;
+ return vts;
+}
+
+static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
+{
+ return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
+}
+
+static int ov5640_get_light_freq(struct ov5640_dev *sensor)
+{
+ /* get banding filter value */
+ int ret, light_freq = 0;
+ u8 temp, temp1;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
+ if (ret)
+ return ret;
+
+ if (temp & 0x80) {
+ /* manual */
+ ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
+ &temp1);
+ if (ret)
+ return ret;
+ if (temp1 & 0x04) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ light_freq = 60;
+ }
+ } else {
+ /* auto */
+ ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
+ &temp1);
+ if (ret)
+ return ret;
+
+ if (temp1 & 0x01) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ }
+ }
+
+ return light_freq;
+}
+
+static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
+{
+ u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
+ int ret;
+
+ /* read preview PCLK */
+ ret = ov5640_get_sysclk(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+ sensor->prev_sysclk = ret;
+ /* read preview HTS */
+ ret = ov5640_get_hts(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+ sensor->prev_hts = ret;
+
+ /* read preview VTS */
+ ret = ov5640_get_vts(sensor);
+ if (ret < 0)
+ return ret;
+ prev_vts = ret;
+
+
+ /* calculate banding filter */
+ /* 60Hz */
+ band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
+ ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
+ if (ret)
+ return ret;
+ if (!band_step60)
+ return -EINVAL;
+ max_band60 = (int)((prev_vts - 4) / band_step60);
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
+ if (ret)
+ return ret;
+
+ /* 50Hz */
+ band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
+ ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
+ if (ret)
+ return ret;
+ if (!band_step50)
+ return -EINVAL;
+ max_band50 = (int)((prev_vts - 4) / band_step50);
+ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
+}
+
+static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
+{
+ /* stable in high */
+ u32 fast_high, fast_low;
+ int ret;
+
+ sensor->ae_low = target * 23 / 25; /* 0.92 */
+ sensor->ae_high = target * 27 / 25; /* 1.08 */
+
+ fast_high = sensor->ae_high << 1;
+ if (fast_high > 255)
+ fast_high = 255;
+
+ fast_low = sensor->ae_low >> 1;
+
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
+ if (ret)
+ return ret;
+ return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
+}
+
+static int ov5640_binning_on(struct ov5640_dev *sensor)
+{
+ u8 temp;
+ int ret;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
+ if (ret)
+ return ret;
+ temp &= 0xfe;
+ return temp ? 1 : 0;
+}
+
+static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
+{
+ u8 temp, channel = virtual_channel;
+ int ret;
+
+ if (channel > 3)
+ return -EINVAL;
+
+ ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
+ if (ret)
+ return ret;
+ temp &= ~(3 << 6);
+ temp |= (channel << 6);
+ return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
+}
+
+static const struct ov5640_mode_info *
+ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
+ int width, int height, bool nearest)
+{
+ const struct ov5640_mode_info *mode = NULL;
+ int i;
+
+ for (i = OV5640_NUM_MODES - 1; i >= 0; i--) {
+ mode = &ov5640_mode_data[fr][i];
+
+ if (!mode->reg_data)
+ continue;
+
+ if ((nearest && mode->width <= width &&
+ mode->height <= height) ||
+ (!nearest && mode->width == width &&
+ mode->height == height))
+ break;
+ }
+
+ if (nearest && i < 0)
+ mode = &ov5640_mode_data[fr][0];
+
+ return mode;
+}
+
+/*
+ * sensor changes between scaling and subsampling, go through
+ * exposure calculation
+ */
+static int ov5640_set_mode_exposure_calc(
+ struct ov5640_dev *sensor, const struct ov5640_mode_info *mode)
+{
+ u32 prev_shutter, prev_gain16;
+ u32 cap_shutter, cap_gain16;
+ u32 cap_sysclk, cap_hts, cap_vts;
+ u32 light_freq, cap_bandfilt, cap_maxband;
+ u32 cap_gain16_shutter;
+ u8 average;
+ int ret;
+
+ if (mode->reg_data == NULL)
+ return -EINVAL;
+
+ /* read preview shutter */
+ ret = ov5640_get_exposure(sensor);
+ if (ret < 0)
+ return ret;
+ prev_shutter = ret;
+ ret = ov5640_binning_on(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
+ mode->id != OV5640_MODE_1080P_1920_1080)
+ prev_shutter *= 2;
+
+ /* read preview gain */
+ ret = ov5640_get_gain(sensor);
+ if (ret < 0)
+ return ret;
+ prev_gain16 = ret;
+
+ /* get average */
+ ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
+ if (ret)
+ return ret;
+
+ /* turn off night mode for capture */
+ ret = ov5640_set_night_mode(sensor);
+ if (ret < 0)
+ return ret;
+
+ /* Write capture setting */
+ ret = ov5640_load_regs(sensor, mode);
+ if (ret < 0)
+ return ret;
+
+ /* read capture VTS */
+ ret = ov5640_get_vts(sensor);
+ if (ret < 0)
+ return ret;
+ cap_vts = ret;
+ ret = ov5640_get_hts(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+ cap_hts = ret;
+
+ ret = ov5640_get_sysclk(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+ cap_sysclk = ret;
+
+ /* calculate capture banding filter */
+ ret = ov5640_get_light_freq(sensor);
+ if (ret < 0)
+ return ret;
+ light_freq = ret;
+
+ if (light_freq == 60) {
+ /* 60Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
+ } else {
+ /* 50Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_hts;
+ }
+
+ if (!sensor->prev_sysclk) {
+ ret = ov5640_get_sysclk(sensor);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+ sensor->prev_sysclk = ret;
+ }
+
+ if (!cap_bandfilt)
+ return -EINVAL;
+
+ cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
+
+ /* calculate capture shutter/gain16 */
+ if (average > sensor->ae_low && average < sensor->ae_high) {
+ /* in stable range */
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter *
+ cap_sysclk / sensor->prev_sysclk *
+ sensor->prev_hts / cap_hts *
+ sensor->ae_target / average;
+ } else {
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter *
+ cap_sysclk / sensor->prev_sysclk *
+ sensor->prev_hts / cap_hts;
+ }
+
+ /* gain to shutter */
+ if (cap_gain16_shutter < (cap_bandfilt * 16)) {
+ /* shutter < 1/100 */
+ cap_shutter = cap_gain16_shutter / 16;
+ if (cap_shutter < 1)
+ cap_shutter = 1;
+
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ if (cap_gain16 < 16)
+ cap_gain16 = 16;
+ } else {
+ if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
+ /* exposure reach max */
+ cap_shutter = cap_bandfilt * cap_maxband;
+ if (!cap_shutter)
+ return -EINVAL;
+
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ } else {
+ /* 1/100 < (cap_shutter = n/100) =< max */
+ cap_shutter =
+ ((int)(cap_gain16_shutter / 16 / cap_bandfilt))
+ * cap_bandfilt;
+ if (!cap_shutter)
+ return -EINVAL;
+
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ }
+ }
+
+ /* set capture gain */
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16);
+ if (ret)
+ return ret;
+
+ /* write capture shutter */
+ if (cap_shutter > (cap_vts - 4)) {
+ cap_vts = cap_shutter + 4;
+ ret = ov5640_set_vts(sensor, cap_vts);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* set exposure */
+ return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter);
+}
+
+/*
+ * if sensor changes inside scaling or subsampling
+ * change mode directly
+ */
+static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
+{
+ int ret;
+
+ if (mode->reg_data == NULL)
+ return -EINVAL;
+
+ /* Write capture setting */
+ ret = ov5640_load_regs(sensor, mode);
+ if (ret < 0)
+ return ret;
+
+ /* turn auto gain/exposure back on for direct mode */
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
+ if (ret)
+ return ret;
+ return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_AUTO);
+}
+
+static int ov5640_set_mode(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *orig_mode)
+{
+ const struct ov5640_mode_info *mode = sensor->current_mode;
+ enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+ int ret;
+
+ dn_mode = mode->dn_mode;
+ orig_dn_mode = orig_mode->dn_mode;
+
+ /* auto gain and exposure must be turned off when changing modes */
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0);
+ if (ret)
+ return ret;
+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL);
+ if (ret)
+ return ret;
+
+ if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
+ (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
+ /*
+ * change between subsampling and scaling
+ * go through exposure calucation
+ */
+ ret = ov5640_set_mode_exposure_calc(sensor, mode);
+ } else {
+ /*
+ * change inside subsampling or scaling
+ * download firmware directly
+ */
+ ret = ov5640_set_mode_direct(sensor, mode);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_set_ae_target(sensor, sensor->ae_target);
+ if (ret < 0)
+ return ret;
+ ret = ov5640_get_light_freq(sensor);
+ if (ret < 0)
+ return ret;
+ ret = ov5640_set_bandingfilter(sensor);
+ if (ret < 0)
+ return ret;
+ ret = ov5640_set_virtual_channel(sensor);
+ if (ret < 0)
+ return ret;
+
+ sensor->pending_mode_change = false;
+
+ return 0;
+}
+
+/* restore the last set video mode after chip power-on */
+static int ov5640_restore_mode(struct ov5640_dev *sensor)
+{
+ int ret;
+
+ /* first load the initial register values */
+ ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
+ if (ret < 0)
+ return ret;
+
+ /* now restore the last capture mode */
+ return ov5640_set_mode(sensor, &ov5640_mode_init_data);
+}
+
+static void ov5640_power(struct ov5640_dev *sensor, bool enable)
+{
+ if (sensor->pwdn_gpio)
+ gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+static void ov5640_reset(struct ov5640_dev *sensor)
+{
+ if (!sensor->reset_gpio)
+ return;
+
+ gpiod_set_value(sensor->reset_gpio, 0);
+
+ /* camera power cycle */
+ ov5640_power(sensor, false);
+ usleep_range(5000, 10000);
+ ov5640_power(sensor, true);
+ usleep_range(5000, 10000);
+
+ gpiod_set_value(sensor->reset_gpio, 1);
+ usleep_range(1000, 2000);
+
+ gpiod_set_value(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+}
+
+static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
+{
+ int ret = 0;
+
+ if (on) {
+ clk_prepare_enable(sensor->xclk);
+
+ ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret)
+ goto xclk_off;
+
+ ov5640_reset(sensor);
+ ov5640_power(sensor, true);
+
+ ret = ov5640_init_slave_id(sensor);
+ if (ret)
+ goto power_off;
+
+ ret = ov5640_restore_mode(sensor);
+ if (ret)
+ goto power_off;
+
+ /*
+ * start streaming briefly followed by stream off in
+ * order to coax the clock lane into LP-11 state.
+ */
+ ret = ov5640_set_stream(sensor, true);
+ if (ret)
+ goto power_off;
+ usleep_range(1000, 2000);
+ ret = ov5640_set_stream(sensor, false);
+ if (ret)
+ goto power_off;
+
+ return 0;
+ }
+
+power_off:
+ ov5640_power(sensor, false);
+ regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
+xclk_off:
+ clk_disable_unprepare(sensor->xclk);
+ return ret;
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (sensor->power_count == !on) {
+ ret = ov5640_set_power(sensor, !!on);
+ if (ret)
+ goto out;
+ }
+
+ /* Update the power count. */
+ sensor->power_count += on ? 1 : -1;
+ WARN_ON(sensor->power_count < 0);
+out:
+ mutex_unlock(&sensor->lock);
+
+ if (on && !ret && sensor->power_count == 1) {
+ /* restore controls */
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+ }
+
+ return ret;
+}
+
+static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
+ struct v4l2_fract *fi,
+ u32 width, u32 height)
+{
+ const struct ov5640_mode_info *mode;
+ u32 minfps, maxfps, fps;
+ int ret;
+
+ minfps = ov5640_framerates[OV5640_15_FPS];
+ maxfps = ov5640_framerates[OV5640_30_FPS];
+
+ if (fi->numerator == 0) {
+ fi->denominator = maxfps;
+ fi->numerator = 1;
+ return OV5640_30_FPS;
+ }
+
+ fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator);
+
+ fi->numerator = 1;
+ if (fps > maxfps)
+ fi->denominator = maxfps;
+ else if (fps < minfps)
+ fi->denominator = minfps;
+ else if (2 * fps >= 2 * minfps + (maxfps - minfps))
+ fi->denominator = maxfps;
+ else
+ fi->denominator = minfps;
+
+ ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS;
+
+ mode = ov5640_find_mode(sensor, ret, width, height, false);
+ return mode ? ret : -EINVAL;
+}
+
+static int ov5640_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (format->pad != 0)
+ return -EINVAL;
+
+ mutex_lock(&sensor->lock);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg,
+ format->pad);
+ else
+ fmt = &sensor->fmt;
+
+ format->format = *fmt;
+
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ enum ov5640_frame_rate fr,
+ const struct ov5640_mode_info **new_mode)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_mode_info *mode;
+
+ mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
+ if (!mode)
+ return -EINVAL;
+
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->code = sensor->fmt.code;
+
+ if (new_mode)
+ *new_mode = mode;
+ return 0;
+}
+
+static int ov5640_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_mode_info *new_mode;
+ int ret;
+
+ if (format->pad != 0)
+ return -EINVAL;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = ov5640_try_fmt_internal(sd, &format->format,
+ sensor->current_fr, &new_mode);
+ if (ret)
+ goto out;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(sd, cfg, 0);
+
+ *fmt = format->format;
+ goto out;
+ }
+
+ sensor->current_mode = new_mode;
+ sensor->fmt = format->format;
+ sensor->pending_mode_change = true;
+out:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+
+/*
+ * Sensor Controls.
+ */
+
+static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
+{
+ int ret;
+
+ if (value) {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
+ BIT(0), BIT(0));
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
+ } else {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
+{
+ int ret;
+
+ if (value) {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
+ BIT(2), BIT(2));
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
+ value & 0xff);
+ } else {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
+{
+ int ret;
+
+ if (value) {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
+ BIT(1), BIT(1));
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
+ value & 0xff);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
+ value & 0xff);
+ } else {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
+{
+ int ret;
+
+ ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
+ BIT(0), awb ? 0 : 1);
+ if (ret)
+ return ret;
+
+ if (!awb) {
+ u16 red = (u16)sensor->ctrls.red_balance->val;
+ u16 blue = (u16)sensor->ctrls.blue_balance->val;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
+ if (ret)
+ return ret;
+ ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, int exp)
+{
+ struct ov5640_ctrls *ctrls = &sensor->ctrls;
+ bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO);
+ int ret = 0;
+
+ if (ctrls->auto_exp->is_new) {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(0), auto_exposure ? 0 : BIT(0));
+ if (ret)
+ return ret;
+ }
+
+ if (!auto_exposure && ctrls->exposure->is_new) {
+ u16 max_exp;
+
+ ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
+ &max_exp);
+ if (ret)
+ return ret;
+ ret = ov5640_get_vts(sensor);
+ if (ret < 0)
+ return ret;
+ max_exp += ret;
+
+ if (ctrls->exposure->val < max_exp)
+ ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, int auto_gain)
+{
+ struct ov5640_ctrls *ctrls = &sensor->ctrls;
+ int ret = 0;
+
+ if (ctrls->auto_gain->is_new) {
+ ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(1), ctrls->auto_gain->val ? 0 : BIT(1));
+ if (ret)
+ return ret;
+ }
+
+ if (!auto_gain && ctrls->gain->is_new) {
+ u16 gain = (u16)ctrls->gain->val;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
+ gain & 0x3ff);
+ }
+
+ return ret;
+}
+
+static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
+{
+ return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
+ 0xa4, value ? 0xa4 : 0);
+}
+
+static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ int val;
+
+ /* v4l2_ctrl_lock() locks our own mutex */
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ if (!ctrl->val)
+ return 0;
+ val = ov5640_get_gain(sensor);
+ if (val < 0)
+ return val;
+ sensor->ctrls.gain->val = val;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL)
+ return 0;
+ val = ov5640_get_exposure(sensor);
+ if (val < 0)
+ return val;
+ sensor->ctrls.exposure->val = val;
+ break;
+ }
+
+ return 0;
+}
+
+static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ int ret;
+
+ /* v4l2_ctrl_lock() locks our own mutex */
+
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (sensor->power_count == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
+ .g_volatile_ctrl = ov5640_g_volatile_ctrl,
+ .s_ctrl = ov5640_s_ctrl,
+};
+
+static const char * const test_pattern_menu[] = {
+ "Disabled",
+ "Color bars",
+};
+
+static int ov5640_init_controls(struct ov5640_dev *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
+ struct ov5640_ctrls *ctrls = &sensor->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 32);
+
+ /* we can use our own mutex for the ctrl lock */
+ hdl->lock = &sensor->lock;
+
+ /* Auto/manual white balance */
+ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
+ 0, 4095, 1, 0);
+ ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
+ 0, 4095, 1, 0);
+ /* Auto/manual exposure */
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 65535, 1, 0);
+ /* Auto/manual gain */
+ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
+ 0, 1, 1, 1);
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 1023, 1, 0);
+
+ ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
+ 0, 255, 1, 64);
+ ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
+ 0, 359, 1, 0);
+ ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
+ 0, 255, 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);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_ctrls;
+ }
+
+ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
+
+ sensor->sd.ctrl_handler = hdl;
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+}
+
+static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->pad != 0)
+ return -EINVAL;
+ if (fse->index >= OV5640_NUM_MODES)
+ return -EINVAL;
+
+ fse->min_width = fse->max_width =
+ ov5640_mode_data[0][fse->index].width;
+ fse->min_height = fse->max_height =
+ ov5640_mode_data[0][fse->index].height;
+
+ return 0;
+}
+
+static int ov5640_enum_frame_interval(
+ struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ struct v4l2_fract tpf;
+ int ret;
+
+ if (fie->pad != 0)
+ return -EINVAL;
+ if (fie->index >= OV5640_NUM_FRAMERATES)
+ return -EINVAL;
+
+ tpf.numerator = 1;
+ tpf.denominator = ov5640_framerates[fie->index];
+
+ ret = ov5640_try_frame_interval(sensor, &tpf,
+ fie->width, fie->height);
+ if (ret < 0)
+ return -EINVAL;
+
+ fie->interval = tpf;
+ return 0;
+}
+
+static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+ mutex_lock(&sensor->lock);
+ fi->interval = sensor->frame_interval;
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ const struct ov5640_mode_info *mode;
+ int frame_rate, ret = 0;
+
+ if (fi->pad != 0)
+ return -EINVAL;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ mode = sensor->current_mode;
+
+ frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
+ mode->width, mode->height);
+ if (frame_rate < 0)
+ frame_rate = OV5640_15_FPS;
+
+ sensor->current_fr = frame_rate;
+ sensor->frame_interval = fi->interval;
+ sensor->pending_mode_change = true;
+out:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+ if (code->pad != 0)
+ return -EINVAL;
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = sensor->fmt.code;
+
+ return 0;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming == !enable) {
+ if (enable && sensor->pending_mode_change) {
+ ret = ov5640_set_mode(sensor, sensor->current_mode);
+ if (ret)
+ goto out;
+ }
+
+ ret = ov5640_set_stream(sensor, enable);
+ if (!ret)
+ sensor->streaming = enable;
+ }
+out:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops ov5640_core_ops = {
+ .s_power = ov5640_s_power,
+};
+
+static const struct v4l2_subdev_video_ops ov5640_video_ops = {
+ .g_frame_interval = ov5640_g_frame_interval,
+ .s_frame_interval = ov5640_s_frame_interval,
+ .s_stream = ov5640_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
+ .enum_mbus_code = ov5640_enum_mbus_code,
+ .get_fmt = ov5640_get_fmt,
+ .set_fmt = ov5640_set_fmt,
+ .enum_frame_size = ov5640_enum_frame_size,
+ .enum_frame_interval = ov5640_enum_frame_interval,
+};
+
+static const struct v4l2_subdev_ops ov5640_subdev_ops = {
+ .core = &ov5640_core_ops,
+ .video = &ov5640_video_ops,
+ .pad = &ov5640_pad_ops,
+};
+
+static int ov5640_get_regulators(struct ov5640_dev *sensor)
+{
+ int i;
+
+ for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = ov5640_supply_name[i];
+
+ return devm_regulator_bulk_get(&sensor->i2c_client->dev,
+ OV5640_NUM_SUPPLIES,
+ sensor->supplies);
+}
+
+static int ov5640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct fwnode_handle *endpoint;
+ struct ov5640_dev *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->i2c_client = client;
+ sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ sensor->fmt.width = 640;
+ sensor->fmt.height = 480;
+ sensor->fmt.field = V4L2_FIELD_NONE;
+ sensor->frame_interval.numerator = 1;
+ sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
+ sensor->current_fr = OV5640_30_FPS;
+ sensor->current_mode =
+ &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
+ sensor->pending_mode_change = true;
+
+ sensor->ae_target = 52;
+
+ endpoint = fwnode_graph_get_next_endpoint(
+ of_fwnode_handle(client->dev.of_node), NULL);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
+ fwnode_handle_put(endpoint);
+ if (ret) {
+ dev_err(dev, "Could not parse endpoint\n");
+ return ret;
+ }
+
+ if (sensor->ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
+ return -EINVAL;
+ }
+
+ /* get system clock (xclk) */
+ sensor->xclk = devm_clk_get(dev, "xclk");
+ if (IS_ERR(sensor->xclk)) {
+ dev_err(dev, "failed to get xclk\n");
+ return PTR_ERR(sensor->xclk);
+ }
+
+ sensor->xclk_freq = clk_get_rate(sensor->xclk);
+ if (sensor->xclk_freq < OV5640_XCLK_MIN ||
+ sensor->xclk_freq > OV5640_XCLK_MAX) {
+ dev_err(dev, "xclk frequency out of range: %d Hz\n",
+ sensor->xclk_freq);
+ return -EINVAL;
+ }
+
+ /* request optional power down pin */
+ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ /* request optional reset pin */
+ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
+
+ 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)
+ return ret;
+
+ ret = ov5640_get_regulators(sensor);
+ if (ret)
+ return ret;
+
+ mutex_init(&sensor->lock);
+
+ ret = ov5640_init_controls(sensor);
+ if (ret)
+ goto entity_cleanup;
+
+ ret = v4l2_async_register_subdev(&sensor->sd);
+ if (ret)
+ goto free_ctrls;
+
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+entity_cleanup:
+ mutex_destroy(&sensor->lock);
+ media_entity_cleanup(&sensor->sd.entity);
+ return ret;
+}
+
+static int ov5640_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ mutex_destroy(&sensor->lock);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov5640_id[] = {
+ {"ov5640", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static const struct of_device_id ov5640_dt_ids[] = {
+ { .compatible = "ovti,ov5640" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
+
+static struct i2c_driver ov5640_i2c_driver = {
+ .driver = {
+ .name = "ov5640",
+ .of_match_table = ov5640_dt_ids,
+ },
+ .id_table = ov5640_id,
+ .probe = ov5640_probe,
+ .remove = ov5640_remove,
+};
+
+module_i2c_driver(ov5640_i2c_driver);
+
+MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index 57bd591ea54b..d1e844f7f03f 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -39,7 +39,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#define OV5645_VOLTAGE_ANALOG 2800000
@@ -87,7 +87,7 @@ struct ov5645 {
struct device *dev;
struct v4l2_subdev sd;
struct media_pad pad;
- struct v4l2_of_endpoint ep;
+ struct v4l2_fwnode_endpoint ep;
struct v4l2_mbus_framefmt fmt;
struct v4l2_rect crop;
struct clk *xclk;
@@ -1102,7 +1102,8 @@ static int ov5645_probe(struct i2c_client *client,
return -EINVAL;
}
- ret = v4l2_of_parse_endpoint(endpoint, &ov5645->ep);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+ &ov5645->ep);
if (ret < 0) {
dev_err(dev, "parsing endpoint node failed\n");
return ret;
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index f57a0b354cf6..95ce90fdb876 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -25,12 +25,13 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-mediabus.h>
-#include <media/v4l2-of.h>
#define SENSOR_NAME "ov5647"
@@ -510,7 +511,7 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
static int ov5647_parse_dt(struct device_node *np)
{
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct device_node *ep;
int ret;
@@ -519,7 +520,7 @@ static int ov5647_parse_dt(struct device_node *np)
if (!ep)
return -EINVAL;
- ret = v4l2_of_parse_endpoint(ep, &bus_cfg);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
of_node_put(ep);
return ret;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 3844853ab0a0..f434fb2ee6fc 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -24,6 +24,7 @@
#include <linux/media.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/sizes.h>
#include <linux/slab.h>
@@ -35,7 +36,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/i2c/s5c73m3.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include "s5c73m3.h"
@@ -1602,7 +1603,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state)
const struct s5c73m3_platform_data *pdata = dev->platform_data;
struct device_node *node = dev->of_node;
struct device_node *node_ep;
- struct v4l2_of_endpoint ep;
+ struct v4l2_fwnode_endpoint ep;
int ret;
if (!node) {
@@ -1639,7 +1640,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state)
return 0;
}
- ret = v4l2_of_parse_endpoint(node_ep, &ep);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep);
of_node_put(node_ep);
if (ret)
return ret;
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index db82ed05792e..962051b9939d 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -30,7 +30,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
static int debug;
module_param(debug, int, 0644);
@@ -1841,7 +1841,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
{
struct device_node *node = dev->of_node;
struct device_node *node_ep;
- struct v4l2_of_endpoint ep;
+ struct v4l2_fwnode_endpoint ep;
int ret;
if (!node) {
@@ -1868,7 +1868,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
return -EINVAL;
}
- ret = v4l2_of_parse_endpoint(node_ep, &ep);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep);
of_node_put(node_ep);
if (ret)
return ret;
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index faee11383cb7..9fd254a8e20d 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -838,7 +838,7 @@ static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
if (s5k6aa->s_power)
ret = s5k6aa->s_power(1);
- usleep_range(4000, 4000);
+ usleep_range(4000, 5000);
if (s5k6aa_gpio_deassert(s5k6aa, RST))
msleep(20);
diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig
index 3149cda1d0db..f59718d8e51e 100644
--- a/drivers/media/i2c/smiapp/Kconfig
+++ b/drivers/media/i2c/smiapp/Kconfig
@@ -3,5 +3,6 @@ config VIDEO_SMIAPP
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK
depends on MEDIA_CAMERA_SUPPORT
select VIDEO_SMIAPP_PLL
+ select V4L2_FWNODE
---help---
This is a generic driver for SMIA++/SMIA camera modules.
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index f4e92bdfe192..e0b0c032c4ac 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -27,12 +27,13 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/smiapp.h>
#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-of.h>
#include "smiapp.h"
@@ -2784,19 +2785,20 @@ static int __maybe_unused smiapp_resume(struct device *dev)
static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev)
{
struct smiapp_hwconfig *hwcfg;
- struct v4l2_of_endpoint *bus_cfg;
- struct device_node *ep;
+ struct v4l2_fwnode_endpoint *bus_cfg;
+ struct fwnode_handle *ep;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
int i;
int rval;
- if (!dev->of_node)
+ if (!fwnode)
return dev->platform_data;
- ep = of_graph_get_next_endpoint(dev->of_node, NULL);
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
return NULL;
- bus_cfg = v4l2_of_alloc_parse_endpoint(ep);
+ bus_cfg = v4l2_fwnode_endpoint_alloc_parse(ep);
if (IS_ERR(bus_cfg))
goto out_err;
@@ -2817,11 +2819,10 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev)
dev_dbg(dev, "lanes %u\n", hwcfg->lanes);
/* NVM size is not mandatory */
- of_property_read_u32(dev->of_node, "nokia,nvm-size",
- &hwcfg->nvm_size);
+ fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size);
- rval = of_property_read_u32(dev->of_node, "clock-frequency",
- &hwcfg->ext_clk);
+ rval = fwnode_property_read_u32(fwnode, "clock-frequency",
+ &hwcfg->ext_clk);
if (rval) {
dev_warn(dev, "can't get clock-frequency\n");
goto out_err;
@@ -2846,13 +2847,13 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev)
dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]);
}
- v4l2_of_free_endpoint(bus_cfg);
- of_node_put(ep);
+ v4l2_fwnode_endpoint_free(bus_cfg);
+ fwnode_handle_put(ep);
return hwcfg;
out_err:
- v4l2_of_free_endpoint(bus_cfg);
- of_node_put(ep);
+ v4l2_fwnode_endpoint_free(bus_cfg);
+ fwnode_handle_put(ep);
return NULL;
}
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index dbd6d92c589f..d2be64d54b22 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -709,6 +709,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd,
switch (mf->code) {
case MEDIA_BUS_FMT_Y10_1X10:
mf->code = MEDIA_BUS_FMT_Y8_1X8;
+ /* fall through */
case MEDIA_BUS_FMT_Y8_1X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
case MEDIA_BUS_FMT_YUYV8_2X8:
@@ -718,6 +719,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd,
break;
default:
mf->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+ /* fall through */
case MEDIA_BUS_FMT_SBGGR8_1X8:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 0f7b9d1b9c57..806383500313 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -1047,11 +1047,13 @@ static int ov772x_probe(struct i2c_client *client,
return -EINVAL;
}
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_PROTOCOL_MANGLING)) {
dev_err(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
+ "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n");
return -EIO;
}
+ client->flags |= I2C_CLIENT_SCCB;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 3251cba89e8f..5788af238b86 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -33,6 +33,8 @@
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/of_graph.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <linux/v4l2-dv-timings.h>
@@ -41,7 +43,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/i2c/tc358743.h>
#include "tc358743_regs.h"
@@ -61,6 +63,8 @@ MODULE_LICENSE("GPL");
#define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2)
+#define POLL_INTERVAL_MS 1000
+
static const struct v4l2_dv_timings_cap tc358743_timings_cap = {
.type = V4L2_DV_BT_656_1120,
/* keep this initialization for compatibility with GCC < 4.4.6 */
@@ -76,7 +80,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = {
struct tc358743_state {
struct tc358743_platform_data pdata;
- struct v4l2_of_bus_mipi_csi2 bus;
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler hdl;
@@ -91,6 +95,9 @@ struct tc358743_state {
struct delayed_work delayed_work_enable_hotplug;
+ struct timer_list timer;
+ struct work_struct work_i2c_poll;
+
/* edid */
u8 edid_blocks_written;
@@ -1296,7 +1303,6 @@ static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
tc358743_csi_err_int_handler(sd, handled);
i2c_wr16(sd, INTSTATUS, MASK_CSI_INT);
- intstatus &= ~MASK_CSI_INT;
}
intstatus = i2c_rd16(sd, INTSTATUS);
@@ -1319,6 +1325,24 @@ static irqreturn_t tc358743_irq_handler(int irq, void *dev_id)
return handled ? IRQ_HANDLED : IRQ_NONE;
}
+static void tc358743_irq_poll_timer(unsigned long arg)
+{
+ struct tc358743_state *state = (struct tc358743_state *)arg;
+
+ schedule_work(&state->work_i2c_poll);
+
+ mod_timer(&state->timer, jiffies + msecs_to_jiffies(POLL_INTERVAL_MS));
+}
+
+static void tc358743_work_i2c_poll(struct work_struct *work)
+{
+ struct tc358743_state *state = container_of(work,
+ struct tc358743_state, work_i2c_poll);
+ bool handled;
+
+ tc358743_isr(&state->sd, 0, &handled);
+}
+
static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
@@ -1473,6 +1497,23 @@ static int tc358743_s_stream(struct v4l2_subdev *sd, int enable)
/* --------------- PAD OPS --------------- */
+static int tc358743_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->index) {
+ case 0:
+ code->code = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case 1:
+ code->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int tc358743_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -1642,6 +1683,7 @@ static const struct v4l2_subdev_video_ops tc358743_video_ops = {
};
static const struct v4l2_subdev_pad_ops tc358743_pad_ops = {
+ .enum_mbus_code = tc358743_enum_mbus_code,
.set_fmt = tc358743_set_fmt,
.get_fmt = tc358743_get_fmt,
.get_edid = tc358743_g_edid,
@@ -1695,7 +1737,7 @@ static void tc358743_gpio_reset(struct tc358743_state *state)
static int tc358743_probe_of(struct tc358743_state *state)
{
struct device *dev = &state->i2c_client->dev;
- struct v4l2_of_endpoint *endpoint;
+ struct v4l2_fwnode_endpoint *endpoint;
struct device_node *ep;
struct clk *refclk;
u32 bps_pr_lane;
@@ -1715,7 +1757,7 @@ static int tc358743_probe_of(struct tc358743_state *state)
return -EINVAL;
}
- endpoint = v4l2_of_alloc_parse_endpoint(ep);
+ endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep));
if (IS_ERR(endpoint)) {
dev_err(dev, "failed to parse endpoint\n");
return PTR_ERR(endpoint);
@@ -1730,7 +1772,11 @@ static int tc358743_probe_of(struct tc358743_state *state)
state->bus = endpoint->bus.mipi_csi2;
- clk_prepare_enable(refclk);
+ ret = clk_prepare_enable(refclk);
+ if (ret) {
+ dev_err(dev, "Failed! to enable clock\n");
+ goto free_endpoint;
+ }
state->pdata.refclk_hz = clk_get_rate(refclk);
state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS;
@@ -1803,7 +1849,7 @@ static int tc358743_probe_of(struct tc358743_state *state)
disable_clk:
clk_disable_unprepare(refclk);
free_endpoint:
- v4l2_of_free_endpoint(endpoint);
+ v4l2_fwnode_endpoint_free(endpoint);
return ret;
}
#else
@@ -1887,6 +1933,8 @@ static int tc358743_probe(struct i2c_client *client,
if (err < 0)
goto err_hdl;
+ state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24;
+
sd->dev = &client->dev;
err = v4l2_async_register_subdev(sd);
if (err < 0)
@@ -1901,7 +1949,6 @@ static int tc358743_probe(struct i2c_client *client,
tc358743_s_dv_timings(sd, &default_timing);
- state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24;
tc358743_set_csi_color_space(sd);
tc358743_init_interrupts(sd);
@@ -1914,6 +1961,14 @@ static int tc358743_probe(struct i2c_client *client,
"tc358743", state);
if (err)
goto err_work_queues;
+ } else {
+ INIT_WORK(&state->work_i2c_poll,
+ tc358743_work_i2c_poll);
+ state->timer.data = (unsigned long)state;
+ state->timer.function = tc358743_irq_poll_timer;
+ state->timer.expires = jiffies +
+ msecs_to_jiffies(POLL_INTERVAL_MS);
+ add_timer(&state->timer);
}
tc358743_enable_interrupts(sd, tx_5v_power_present(sd));
@@ -1929,6 +1984,8 @@ static int tc358743_probe(struct i2c_client *client,
return 0;
err_work_queues:
+ if (!state->i2c_client->irq)
+ flush_work(&state->work_i2c_poll);
cancel_delayed_work(&state->delayed_work_enable_hotplug);
mutex_destroy(&state->confctl_mutex);
err_hdl:
@@ -1942,6 +1999,10 @@ static int tc358743_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct tc358743_state *state = to_state(sd);
+ if (!state->i2c_client->irq) {
+ del_timer_sync(&state->timer);
+ flush_work(&state->work_i2c_poll);
+ }
cancel_delayed_work(&state->delayed_work_enable_hotplug);
v4l2_async_unregister_subdev(sd);
v4l2_device_unregister_subdev(sd);
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 07853d2252aa..ad2df998f9c5 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -38,7 +38,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-ctrls.h>
#include <media/i2c/tvp514x.h>
#include <media/media-entity.h>
@@ -998,7 +998,7 @@ static struct tvp514x_platform_data *
tvp514x_get_pdata(struct i2c_client *client)
{
struct tvp514x_platform_data *pdata = NULL;
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct device_node *endpoint;
unsigned int flags;
@@ -1009,7 +1009,7 @@ tvp514x_get_pdata(struct i2c_client *client)
if (!endpoint)
return NULL;
- if (v4l2_of_parse_endpoint(endpoint, &bus_cfg))
+ if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg))
goto done;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 04e96b3057bb..7b79a7498751 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -12,10 +12,11 @@
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
#include "tvp5150_reg.h"
@@ -658,7 +659,7 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd,
struct tvp5150 *decoder = to_tvp5150(sd);
v4l2_std_id std = decoder->norm;
u8 reg;
- int pos=0;
+ int pos = 0;
if (std == V4L2_STD_ALL) {
dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n");
@@ -668,33 +669,30 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd,
line += 3;
}
- if (line<6||line>27)
+ if (line < 6 || line > 27)
return 0;
- while (regs->reg != (u16)-1 ) {
+ while (regs->reg != (u16)-1) {
if ((type & regs->type.vbi_type) &&
- (line>=regs->type.ini_line) &&
- (line<=regs->type.end_line)) {
- type=regs->type.vbi_type;
+ (line >= regs->type.ini_line) &&
+ (line <= regs->type.end_line))
break;
- }
regs++;
pos++;
}
+
if (regs->reg == (u16)-1)
return 0;
- type=pos | (flags & 0xf0);
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+ type = pos | (flags & 0xf0);
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- if (fields&1) {
+ if (fields & 1)
tvp5150_write(sd, reg, type);
- }
- if (fields&2) {
- tvp5150_write(sd, reg+1, type);
- }
+ if (fields & 2)
+ tvp5150_write(sd, reg + 1, type);
return type;
}
@@ -1358,7 +1356,7 @@ static int tvp5150_init(struct i2c_client *c)
static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
{
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct device_node *ep;
#ifdef CONFIG_MEDIA_CONTROLLER
struct device_node *connectors, *child;
@@ -1373,7 +1371,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
if (!ep)
return -EINVAL;
- ret = v4l2_of_parse_endpoint(ep, &bus_cfg);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
if (ret)
goto err;
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 4c1190127c85..a26c1a3f7183 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -33,7 +33,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include "tvp7002_reg.h"
@@ -889,7 +889,7 @@ static const struct v4l2_subdev_ops tvp7002_ops = {
static struct tvp7002_config *
tvp7002_get_pdata(struct i2c_client *client)
{
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct tvp7002_config *pdata = NULL;
struct device_node *endpoint;
unsigned int flags;
@@ -901,7 +901,7 @@ tvp7002_get_pdata(struct i2c_client *client)
if (!endpoint)
return NULL;
- if (v4l2_of_parse_endpoint(endpoint, &bus_cfg))
+ if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg))
goto done;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index bc44193efa47..dd0f0ead9516 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -18,6 +18,7 @@
#include <linux/bitmap.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <media/media-entity.h>
#include <media/media-device.h>
@@ -386,6 +387,41 @@ struct media_entity *media_graph_walk_next(struct media_graph *graph)
}
EXPORT_SYMBOL_GPL(media_graph_walk_next);
+int media_entity_get_fwnode_pad(struct media_entity *entity,
+ struct fwnode_handle *fwnode,
+ unsigned long direction_flags)
+{
+ struct fwnode_endpoint endpoint;
+ unsigned int i;
+ int ret;
+
+ if (!entity->ops || !entity->ops->get_fwnode_pad) {
+ for (i = 0; i < entity->num_pads; i++) {
+ if (entity->pads[i].flags & direction_flags)
+ return i;
+ }
+
+ return -ENXIO;
+ }
+
+ ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);
+ if (ret)
+ return ret;
+
+ ret = entity->ops->get_fwnode_pad(&endpoint);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= entity->num_pads)
+ return -ENXIO;
+
+ if (!(entity->pads[ret].flags & direction_flags))
+ return -ENXIO;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);
+
/* -----------------------------------------------------------------------------
* Pipeline management
*/
@@ -530,8 +566,13 @@ void __media_pipeline_stop(struct media_entity *entity)
struct media_graph *graph = &entity->pipe->graph;
struct media_pipeline *pipe = entity->pipe;
+ /*
+ * If the following check fails, the driver has performed an
+ * unbalanced call to media_pipeline_stop()
+ */
+ if (WARN_ON(!pipe))
+ return;
- WARN_ON(!pipe->streaming_count);
media_graph_walk_start(graph, entity);
while ((entity = media_graph_walk_next(graph))) {
diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
index 04d06c564602..90f4263452d3 100644
--- a/drivers/media/pci/bt8xx/dst_ca.c
+++ b/drivers/media/pci/bt8xx/dst_ca.c
@@ -637,6 +637,7 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
+ break;
default:
result = -EOPNOTSUPP;
}
diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c
index d5c911c09e2b..f8e173f3e9e2 100644
--- a/drivers/media/pci/cobalt/cobalt-driver.c
+++ b/drivers/media/pci/cobalt/cobalt-driver.c
@@ -205,6 +205,8 @@ void cobalt_pcie_status_show(struct cobalt *cobalt)
offset = pci_find_capability(pci_dev, PCI_CAP_ID_EXP);
bus_offset = pci_find_capability(pci_bus_dev, PCI_CAP_ID_EXP);
+ if (!offset || !bus_offset)
+ return;
/* Device */
pci_read_config_dword(pci_dev, offset + PCI_EXP_DEVCAP, &capa);
diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c
index 205a98da877c..f68ee57a9ae2 100644
--- a/drivers/media/pci/cx18/cx18-alsa-pcm.c
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c
@@ -257,14 +257,16 @@ static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
unsigned long flags;
+ unsigned char *dma_area = NULL;
spin_lock_irqsave(&cxsc->slock, flags);
if (substream->runtime->dma_area) {
dprintk("freeing pcm capture region\n");
- vfree(substream->runtime->dma_area);
+ dma_area = substream->runtime->dma_area;
substream->runtime->dma_area = NULL;
}
spin_unlock_irqrestore(&cxsc->slock, flags);
+ vfree(dma_area);
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
index d130d65828b0..53f4d6bf81fb 100644
--- a/drivers/media/pci/cx18/cx18-dvb.c
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -151,7 +151,7 @@ static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
}
if (ret) {
- CX18_ERR("The MPC718 board variant with the MT352 DVB-Tdemodualtor will not work without it\n");
+ CX18_ERR("The MPC718 board variant with the MT352 DVB-T demodulator will not work without it\n");
CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware mpc718' if you need the firmware\n");
}
return ret;
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 9e39aea85df6..c48fa8e25a70 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -2081,7 +2081,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- /* break omitted intentionally */
+ /* fall-through */
case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -2238,6 +2238,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
/* Currently only enabled for the integrated IR controller */
if (!enable_885_ir)
break;
+ /* fall-through */
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE:
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index 73cc7a67a8bc..6df21b29ea17 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -3681,7 +3681,14 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
core->nr = nr;
sprintf(core->name, "cx88[%d]", core->nr);
- core->tvnorm = V4L2_STD_NTSC_M;
+ /*
+ * Note: Setting initial standard here would cause first call to
+ * cx88_set_tvnorm() to return without programming any registers. Leave
+ * it blank for at this point and it will get set later in
+ * cx8800_initdev()
+ */
+ core->tvnorm = 0;
+
core->width = 320;
core->height = 240;
core->field = V4L2_FIELD_INTERLACED;
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index c7d4e87ccb64..7d25ecd4404b 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1420,7 +1420,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
request_module("rtc-isl1208");
core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
}
- /* break intentionally omitted */
+ /* fall-through */
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
request_module("ir-kbd-i2c");
}
@@ -1435,7 +1435,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
/* initial device configuration */
mutex_lock(&core->lock);
- cx88_set_tvnorm(core, core->tvnorm);
+ cx88_set_tvnorm(core, V4L2_STD_NTSC_M);
v4l2_ctrl_handler_setup(&core->video_hdl);
v4l2_ctrl_handler_setup(&core->audio_hdl);
cx88_video_mux(core, 0);
diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig
index 44e5dc15e60a..ffed78c2ffb4 100644
--- a/drivers/media/pci/ddbridge/Kconfig
+++ b/drivers/media/pci/ddbridge/Kconfig
@@ -6,6 +6,9 @@ config DVB_DDBRIDGE
select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
---help---
Support for cards with the Digital Devices PCI express bridge:
- Octopus PCIe Bridge
@@ -14,5 +17,8 @@ config DVB_DDBRIDGE
- DuoFlex S2 Octopus
- DuoFlex CT Octopus
- cineS2(v6)
+ - CineCTv6 and DuoFlex CT (STV0367-based)
+ - CineCTv7 and DuoFlex CT2/C2T2/C2T2I (Sony CXD28xx-based)
+ - MaxA8 series
Say Y if you own such a card and want to use it.
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index 340cff02dee2..cd1723e79a07 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -17,6 +17,8 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -39,6 +41,14 @@
#include "stv090x.h"
#include "lnbh24.h"
#include "drxk.h"
+#include "stv0367.h"
+#include "stv0367_priv.h"
+#include "cxd2841er.h"
+#include "tda18212.h"
+
+static int xo2_speed = 2;
+module_param(xo2_speed, int, 0444);
+MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
@@ -47,6 +57,24 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/******************************************************************************/
+static int i2c_io(struct i2c_adapter *adapter, u8 adr,
+ u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = wbuf, .len = wlen },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = rbuf, .len = rlen } };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
{
struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
@@ -54,15 +82,21 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
}
-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+static int i2c_read_regs(struct i2c_adapter *adapter,
+ u8 adr, u8 reg, u8 *val, u8 len)
{
struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
.buf = &reg, .len = 1 },
{.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1 } };
+ .buf = val, .len = len } };
return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
}
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+ return i2c_read_regs(adapter, adr, reg, val, 1);
+}
+
static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
u16 reg, u8 *val)
{
@@ -74,6 +108,27 @@ static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
}
+static int i2c_write_reg(struct i2c_adapter *adap, u8 adr,
+ u8 reg, u8 val)
+{
+ u8 msg[2] = {reg, val};
+
+ return i2c_write(adap, adr, msg, 2);
+}
+
+static inline u32 safe_ddbreadl(struct ddb *dev, u32 adr)
+{
+ u32 val = ddbreadl(adr);
+
+ /* (ddb)readl returns (uint)-1 (all bits set) on failure, catch that */
+ if (val == ~0) {
+ dev_err(&dev->pdev->dev, "ddbreadl failure, adr=%08x\n", adr);
+ return 0;
+ }
+
+ return val;
+}
+
static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
{
struct ddb *dev = i2c->dev;
@@ -84,10 +139,10 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
if (stat == 0) {
- printk(KERN_ERR "I2C timeout\n");
+ dev_err(&dev->pdev->dev, "I2C timeout\n");
{ /* MSI debugging*/
u32 istat = ddbreadl(INTERRUPT_STATUS);
- printk(KERN_ERR "IRS %08x\n", istat);
+ dev_err(&dev->pdev->dev, "IRS %08x\n", istat);
ddbwritel(istat, INTERRUPT_ACK);
}
return -EIO;
@@ -493,7 +548,7 @@ static u32 ddb_input_avail(struct ddb_input *input)
off = (stat & 0x7ff) << 7;
if (ctrl & 4) {
- printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
+ dev_err(&dev->pdev->dev, "IA %d %d %08x\n", idx, off, ctrl);
ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
return 0;
}
@@ -571,6 +626,7 @@ static int demod_attach_drxk(struct ddb_input *input)
struct i2c_adapter *i2c = &input->port->i2c->adap;
struct dvb_frontend *fe;
struct drxk_config config;
+ struct device *dev = &input->port->dev->pdev->dev;
memset(&config, 0, sizeof(config));
config.microcode_name = "drxk_a3.mc";
@@ -579,7 +635,7 @@ static int demod_attach_drxk(struct ddb_input *input)
fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
if (!input->fe) {
- printk(KERN_ERR "No DRXK found!\n");
+ dev_err(dev, "No DRXK found!\n");
return -ENODEV;
}
fe->sec_priv = input;
@@ -592,12 +648,13 @@ static int tuner_attach_tda18271(struct ddb_input *input)
{
struct i2c_adapter *i2c = &input->port->i2c->adap;
struct dvb_frontend *fe;
+ struct device *dev = &input->port->dev->pdev->dev;
if (input->fe->ops.i2c_gate_ctrl)
input->fe->ops.i2c_gate_ctrl(input->fe, 1);
fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
if (!fe) {
- printk(KERN_ERR "No TDA18271 found!\n");
+ dev_err(dev, "No TDA18271 found!\n");
return -ENODEV;
}
if (input->fe->ops.i2c_gate_ctrl)
@@ -609,6 +666,156 @@ static int tuner_attach_tda18271(struct ddb_input *input)
/******************************************************************************/
/******************************************************************************/
+static struct stv0367_config ddb_stv0367_config[] = {
+ {
+ .demod_address = 0x1f,
+ .xtal = 27000000,
+ .if_khz = 0,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1e,
+ .xtal = 27000000,
+ .if_khz = 0,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ },
+};
+
+static int demod_attach_stv0367(struct ddb_input *input)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
+
+ /* attach frontend */
+ input->fe = dvb_attach(stv0367ddb_attach,
+ &ddb_stv0367_config[(input->nr & 1)], i2c);
+
+ if (!input->fe) {
+ dev_err(dev, "stv0367ddb_attach failed (not found?)\n");
+ return -ENODEV;
+ }
+
+ input->fe->sec_priv = input;
+ input->gate_ctrl = input->fe->ops.i2c_gate_ctrl;
+ input->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ return 0;
+}
+
+static int tuner_tda18212_ping(struct ddb_input *input, unsigned short adr)
+{
+ struct i2c_adapter *adapter = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
+
+ u8 tda_id[2];
+ u8 subaddr = 0x00;
+
+ dev_dbg(dev, "stv0367-tda18212 tuner ping\n");
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 1);
+
+ if (i2c_read_regs(adapter, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
+ dev_dbg(dev, "tda18212 ping 1 fail\n");
+ if (i2c_read_regs(adapter, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
+ dev_warn(dev, "tda18212 ping failed, expect problems\n");
+
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 0);
+
+ return 0;
+}
+
+static int demod_attach_cxd28xx(struct ddb_input *input, int par, int osc24)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
+ struct cxd2841er_config cfg;
+
+ /* the cxd2841er driver expects 8bit/shifted I2C addresses */
+ cfg.i2c_addr = ((input->nr & 1) ? 0x6d : 0x6c) << 1;
+
+ cfg.xtal = osc24 ? SONY_XTAL_24000 : SONY_XTAL_20500;
+ cfg.flags = CXD2841ER_AUTO_IFHZ | CXD2841ER_EARLY_TUNE |
+ CXD2841ER_NO_WAIT_LOCK | CXD2841ER_NO_AGCNEG |
+ CXD2841ER_TSBITS;
+
+ if (!par)
+ cfg.flags |= CXD2841ER_TS_SERIAL;
+
+ /* attach frontend */
+ input->fe = dvb_attach(cxd2841er_attach_t_c, &cfg, i2c);
+
+ if (!input->fe) {
+ dev_err(dev, "No Sony CXD28xx found!\n");
+ return -ENODEV;
+ }
+
+ input->fe->sec_priv = input;
+ input->gate_ctrl = input->fe->ops.i2c_gate_ctrl;
+ input->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ return 0;
+}
+
+static int tuner_attach_tda18212(struct ddb_input *input, u32 porttype)
+{
+ struct i2c_adapter *adapter = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
+ struct i2c_client *client;
+ struct tda18212_config config = {
+ .fe = input->fe,
+ .if_dvbt_6 = 3550,
+ .if_dvbt_7 = 3700,
+ .if_dvbt_8 = 4150,
+ .if_dvbt2_6 = 3250,
+ .if_dvbt2_7 = 4000,
+ .if_dvbt2_8 = 4000,
+ .if_dvbc = 5000,
+ };
+ struct i2c_board_info board_info = {
+ .type = "tda18212",
+ .platform_data = &config,
+ };
+
+ if (input->nr & 1)
+ board_info.addr = 0x63;
+ else
+ board_info.addr = 0x60;
+
+ /* due to a hardware quirk with the I2C gate on the stv0367+tda18212
+ * combo, the tda18212 must be probed by reading it's id _twice_ when
+ * cold started, or it very likely will fail.
+ */
+ if (porttype == DDB_TUNER_DVBCT_ST)
+ tuner_tda18212_ping(input, board_info.addr);
+
+ request_module(board_info.type);
+
+ /* perform tuner init/attach */
+ client = i2c_new_device(adapter, &board_info);
+ if (client == NULL || client->dev.driver == NULL)
+ goto err;
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ goto err;
+ }
+
+ input->i2c_client[0] = client;
+
+ return 0;
+err:
+ dev_warn(dev, "TDA18212 tuner not found. Device is not fully operational.\n");
+ return -ENODEV;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
static struct stv090x_config stv0900 = {
.device = STV0900,
.demod_mode = STV090x_DUAL,
@@ -662,19 +869,20 @@ static struct stv6110x_config stv6110b = {
static int demod_attach_stv0900(struct ddb_input *input, int type)
{
struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
input->fe = dvb_attach(stv090x_attach, feconf, i2c,
(input->nr & 1) ? STV090x_DEMODULATOR_1
: STV090x_DEMODULATOR_0);
if (!input->fe) {
- printk(KERN_ERR "No STV0900 found!\n");
+ dev_err(dev, "No STV0900 found!\n");
return -ENODEV;
}
if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
0, (input->nr & 1) ?
(0x09 - type) : (0x0b - type))) {
- printk(KERN_ERR "No LNBH24 found!\n");
+ dev_err(dev, "No LNBH24 found!\n");
return -ENODEV;
}
return 0;
@@ -683,6 +891,7 @@ static int demod_attach_stv0900(struct ddb_input *input, int type)
static int tuner_attach_stv6110(struct ddb_input *input, int type)
{
struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct device *dev = &input->port->dev->pdev->dev;
struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
struct stv6110x_config *tunerconf = (input->nr & 1) ?
&stv6110b : &stv6110a;
@@ -690,10 +899,10 @@ static int tuner_attach_stv6110(struct ddb_input *input, int type)
ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
if (!ctl) {
- printk(KERN_ERR "No STV6110X found!\n");
+ dev_err(dev, "No STV6110X found!\n");
return -ENODEV;
}
- printk(KERN_INFO "attach tuner input %d adr %02x\n",
+ dev_info(dev, "attach tuner input %d adr %02x\n",
input->nr, tunerconf->addr);
feconf->tuner_init = ctl->tuner_init;
@@ -779,19 +988,28 @@ static void dvb_input_detach(struct ddb_input *input)
{
struct dvb_adapter *adap = &input->adap;
struct dvb_demux *dvbdemux = &input->demux;
+ struct i2c_client *client;
switch (input->attached) {
case 5:
- if (input->fe2)
+ client = input->i2c_client[0];
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+ if (input->fe2) {
dvb_unregister_frontend(input->fe2);
+ input->fe2 = NULL;
+ }
if (input->fe) {
dvb_unregister_frontend(input->fe);
dvb_frontend_detach(input->fe);
input->fe = NULL;
}
+ /* fall-through */
case 4:
dvb_net_release(&input->dvbnet);
-
+ /* fall-through */
case 3:
dvbdemux->dmx.close(&dvbdemux->dmx);
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
@@ -799,10 +1017,10 @@ static void dvb_input_detach(struct ddb_input *input)
dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
&input->mem_frontend);
dvb_dmxdev_release(&input->dmxdev);
-
+ /* fall-through */
case 2:
dvb_dmx_release(&input->demux);
-
+ /* fall-through */
case 1:
dvb_unregister_adapter(adap);
}
@@ -815,12 +1033,14 @@ static int dvb_input_attach(struct ddb_input *input)
struct ddb_port *port = input->port;
struct dvb_adapter *adap = &input->adap;
struct dvb_demux *dvbdemux = &input->demux;
+ struct device *dev = &input->port->dev->pdev->dev;
+ int sony_osc24 = 0, sony_tspar = 0;
ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
&input->port->dev->pdev->dev,
adapter_nr);
if (ret < 0) {
- printk(KERN_ERR "ddbridge: Could not register adapter.Check if you enabled enough adapters in dvb-core!\n");
+ dev_err(dev, "Could not register adapter. Check if you enabled enough adapters in dvb-core!\n");
return ret;
}
input->attached = 1;
@@ -882,7 +1102,56 @@ static int dvb_input_attach(struct ddb_input *input)
sizeof(struct dvb_tuner_ops));
}
break;
+ case DDB_TUNER_DVBCT_ST:
+ if (demod_attach_stv0367(input) < 0)
+ return -ENODEV;
+ if (tuner_attach_tda18212(input, port->type) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_DVBC2T2I_SONY_P:
+ case DDB_TUNER_DVBCT2_SONY_P:
+ case DDB_TUNER_DVBC2T2_SONY_P:
+ case DDB_TUNER_ISDBT_SONY_P:
+ if (port->type == DDB_TUNER_DVBC2T2I_SONY_P)
+ sony_osc24 = 1;
+ if (input->port->dev->info->ts_quirks & TS_QUIRK_ALT_OSC)
+ sony_osc24 = 0;
+ if (input->port->dev->info->ts_quirks & TS_QUIRK_SERIAL)
+ sony_tspar = 0;
+ else
+ sony_tspar = 1;
+
+ if (demod_attach_cxd28xx(input, sony_tspar, sony_osc24) < 0)
+ return -ENODEV;
+ if (tuner_attach_tda18212(input, port->type) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_XO2_DVBC2T2I_SONY:
+ case DDB_TUNER_XO2_DVBCT2_SONY:
+ case DDB_TUNER_XO2_DVBC2T2_SONY:
+ case DDB_TUNER_XO2_ISDBT_SONY:
+ if (port->type == DDB_TUNER_XO2_DVBC2T2I_SONY)
+ sony_osc24 = 1;
+
+ if (demod_attach_cxd28xx(input, 0, sony_osc24) < 0)
+ return -ENODEV;
+ if (tuner_attach_tda18212(input, port->type) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
}
+
input->attached = 5;
return 0;
}
@@ -997,9 +1266,9 @@ static void input_tasklet(unsigned long data)
if (input->port->class == DDB_PORT_TUNER) {
if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
- printk(KERN_ERR "Overflow input %d\n", input->nr);
+ dev_err(&dev->pdev->dev, "Overflow input %d\n", input->nr);
while (input->cbuf != ((input->stat >> 11) & 0x1f)
- || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
+ || (4 & safe_ddbreadl(dev, DMA_BUFFER_CONTROL(input->nr)))) {
dvb_dmx_swfilter_packets(&input->demux,
input->vbuf[input->cbuf],
input->dma_buf_size / 188);
@@ -1036,6 +1305,7 @@ static struct cxd2099_cfg cxd_cfg = {
.adr = 0x40,
.polarity = 1,
.clock_mode = 1,
+ .max_i2c = 512,
};
static int ddb_ci_attach(struct ddb_port *port)
@@ -1066,6 +1336,7 @@ static int ddb_ci_attach(struct ddb_port *port)
static int ddb_port_attach(struct ddb_port *port)
{
+ struct device *dev = &port->dev->pdev->dev;
int ret = 0;
switch (port->class) {
@@ -1082,7 +1353,7 @@ static int ddb_port_attach(struct ddb_port *port)
break;
}
if (ret < 0)
- printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
+ dev_err(dev, "port_attach on port %d failed\n", port->nr);
return ret;
}
@@ -1130,6 +1401,71 @@ static void ddb_ports_detach(struct ddb *dev)
/****************************************************************************/
/****************************************************************************/
+static int init_xo2(struct ddb_port *port)
+{
+ struct i2c_adapter *i2c = &port->i2c->adap;
+ struct device *dev = &port->dev->pdev->dev;
+ u8 val, data[2];
+ int res;
+
+ res = i2c_read_regs(i2c, 0x10, 0x04, data, 2);
+ if (res < 0)
+ return res;
+
+ if (data[0] != 0x01) {
+ dev_info(dev, "Port %d: invalid XO2\n", port->nr);
+ return -1;
+ }
+
+ i2c_read_reg(i2c, 0x10, 0x08, &val);
+ if (val != 0) {
+ i2c_write_reg(i2c, 0x10, 0x08, 0x00);
+ msleep(100);
+ }
+ /* Enable tuner power, disable pll, reset demods */
+ i2c_write_reg(i2c, 0x10, 0x08, 0x04);
+ usleep_range(2000, 3000);
+ /* Release demod resets */
+ i2c_write_reg(i2c, 0x10, 0x08, 0x07);
+
+ /* speed: 0=55,1=75,2=90,3=104 MBit/s */
+ i2c_write_reg(i2c, 0x10, 0x09,
+ ((xo2_speed >= 0 && xo2_speed <= 3) ? xo2_speed : 2));
+
+ i2c_write_reg(i2c, 0x10, 0x0a, 0x01);
+ i2c_write_reg(i2c, 0x10, 0x0b, 0x01);
+
+ usleep_range(2000, 3000);
+ /* Start XO2 PLL */
+ i2c_write_reg(i2c, 0x10, 0x08, 0x87);
+
+ return 0;
+}
+
+static int port_has_xo2(struct ddb_port *port, u8 *type, u8 *id)
+{
+ u8 probe[1] = { 0x00 }, data[4];
+
+ *type = DDB_XO2_TYPE_NONE;
+
+ if (i2c_io(&port->i2c->adap, 0x10, probe, 1, data, 4))
+ return 0;
+ if (data[0] == 'D' && data[1] == 'F') {
+ *id = data[2];
+ *type = DDB_XO2_TYPE_DUOFLEX;
+ return 1;
+ }
+ if (data[0] == 'C' && data[1] == 'I') {
+ *id = data[2];
+ *type = DDB_XO2_TYPE_CI;
+ return 1;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
static int port_has_ci(struct ddb_port *port)
{
u8 val;
@@ -1162,10 +1498,39 @@ static int port_has_drxks(struct ddb_port *port)
return 1;
}
+static int port_has_stv0367(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read_reg16(&port->i2c->adap, 0x1e, 0xf000, &val) < 0)
+ return 0;
+ if (val != 0x60)
+ return 0;
+ if (i2c_read_reg16(&port->i2c->adap, 0x1f, 0xf000, &val) < 0)
+ return 0;
+ if (val != 0x60)
+ return 0;
+ return 1;
+}
+
+static int port_has_cxd28xx(struct ddb_port *port, u8 *id)
+{
+ struct i2c_adapter *i2c = &port->i2c->adap;
+ int status;
+
+ status = i2c_write_reg(&port->i2c->adap, 0x6e, 0, 0);
+ if (status)
+ return 0;
+ status = i2c_read_reg(i2c, 0x6e, 0xfd, id);
+ if (status)
+ return 0;
+ return 1;
+}
+
static void ddb_port_probe(struct ddb_port *port)
{
struct ddb *dev = port->dev;
char *modname = "NO MODULE";
+ u8 xo2_type, xo2_id, cxd_id;
port->class = DDB_PORT_NONE;
@@ -1173,6 +1538,85 @@ static void ddb_port_probe(struct ddb_port *port)
modname = "CI";
port->class = DDB_PORT_CI;
ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_xo2(port, &xo2_type, &xo2_id)) {
+ dev_dbg(&dev->pdev->dev, "Port %d (TAB %d): XO2 type: %d, id: %d\n",
+ port->nr, port->nr+1, xo2_type, xo2_id);
+
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+
+ switch (xo2_type) {
+ case DDB_XO2_TYPE_DUOFLEX:
+ init_xo2(port);
+ switch (xo2_id >> 2) {
+ case 0:
+ modname = "DUAL DVB-S2 (unsupported)";
+ port->class = DDB_PORT_NONE;
+ port->type = DDB_TUNER_XO2_DVBS_STV0910;
+ break;
+ case 1:
+ modname = "DUAL DVB-C/T/T2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_XO2_DVBCT2_SONY;
+ break;
+ case 2:
+ modname = "DUAL DVB-ISDBT";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_XO2_ISDBT_SONY;
+ break;
+ case 3:
+ modname = "DUAL DVB-C/C2/T/T2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_XO2_DVBC2T2_SONY;
+ break;
+ case 4:
+ modname = "DUAL ATSC (unsupported)";
+ port->class = DDB_PORT_NONE;
+ port->type = DDB_TUNER_XO2_ATSC_ST;
+ break;
+ case 5:
+ modname = "DUAL DVB-C/C2/T/T2/ISDBT";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_XO2_DVBC2T2I_SONY;
+ break;
+ default:
+ modname = "Unknown XO2 DuoFlex module\n";
+ break;
+ }
+ break;
+ case DDB_XO2_TYPE_CI:
+ dev_info(&dev->pdev->dev, "DuoFlex CI modules not supported\n");
+ break;
+ default:
+ dev_info(&dev->pdev->dev, "Unknown XO2 DuoFlex module\n");
+ break;
+ }
+ } else if (port_has_cxd28xx(port, &cxd_id)) {
+ switch (cxd_id) {
+ case 0xa4:
+ modname = "DUAL DVB-C2T2 CXD2843";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBC2T2_SONY_P;
+ break;
+ case 0xb1:
+ modname = "DUAL DVB-CT2 CXD2837";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBCT2_SONY_P;
+ break;
+ case 0xb0:
+ modname = "DUAL ISDB-T CXD2838";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_ISDBT_SONY_P;
+ break;
+ case 0xc1:
+ modname = "DUAL DVB-C2T2 ISDB-T CXD2854";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBC2T2I_SONY_P;
+ break;
+ default:
+ modname = "Unknown CXD28xx tuner";
+ break;
+ }
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
} else if (port_has_stv0900(port)) {
modname = "DUAL DVB-S2";
port->class = DDB_PORT_TUNER;
@@ -1188,8 +1632,14 @@ static void ddb_port_probe(struct ddb_port *port)
port->class = DDB_PORT_TUNER;
port->type = DDB_TUNER_DVBCT_TR;
ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_stv0367(port)) {
+ modname = "DUAL DVB-C/T";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBCT_ST;
+ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
}
- printk(KERN_INFO "Port %d (TAB %d): %s\n",
+
+ dev_info(&dev->pdev->dev, "Port %d (TAB %d): %s\n",
port->nr, port->nr+1, modname);
}
@@ -1343,7 +1793,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
wbuf += 4;
wlen -= 4;
ddbwritel(data, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
+ while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004)
;
}
@@ -1363,7 +1813,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
if (shift)
data <<= shift;
ddbwritel(data, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
+ while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004)
;
if (!rlen) {
@@ -1375,7 +1825,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
while (rlen > 4) {
ddbwritel(0xffffffff, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
+ while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004)
;
data = ddbreadl(SPI_DATA);
*(u32 *) rbuf = swab32(data);
@@ -1384,7 +1834,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
}
ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
ddbwritel(0xffffffff, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
+ while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004)
;
data = ddbreadl(SPI_DATA);
@@ -1571,7 +2021,7 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
dev->info = (struct ddb_info *) id->driver_data;
- printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
+ dev_info(&pdev->dev, "Detected %s\n", dev->info->name);
dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
pci_resource_len(dev->pdev, 0));
@@ -1579,13 +2029,13 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
stat = -ENOMEM;
goto fail;
}
- printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
+ dev_info(&pdev->dev, "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
#ifdef CONFIG_PCI_MSI
if (pci_msi_enabled())
stat = pci_enable_msi(dev->pdev);
if (stat) {
- printk(KERN_INFO ": MSI not available.\n");
+ dev_info(&pdev->dev, "MSI not available.\n");
} else {
irq_flag = 0;
dev->msi = 1;
@@ -1601,11 +2051,24 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ddbwritel(0xfff0f, INTERRUPT_ENABLE);
ddbwritel(0, MSI1_ENABLE);
+ /* board control */
+ if (dev->info->board_control) {
+ ddbwritel(0, DDB_LINK_TAG(0) | BOARD_CONTROL);
+ msleep(100);
+ ddbwritel(dev->info->board_control_2,
+ DDB_LINK_TAG(0) | BOARD_CONTROL);
+ usleep_range(2000, 3000);
+ ddbwritel(dev->info->board_control_2
+ | dev->info->board_control,
+ DDB_LINK_TAG(0) | BOARD_CONTROL);
+ usleep_range(2000, 3000);
+ }
+
if (ddb_i2c_init(dev) < 0)
goto fail1;
ddb_ports_init(dev);
if (ddb_buffers_alloc(dev) < 0) {
- printk(KERN_INFO ": Could not allocate buffer memory\n");
+ dev_err(&pdev->dev, "Could not allocate buffer memory\n");
goto fail2;
}
if (ddb_ports_attach(dev) < 0)
@@ -1615,19 +2078,19 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
fail3:
ddb_ports_detach(dev);
- printk(KERN_ERR "fail3\n");
+ dev_err(&pdev->dev, "fail3\n");
ddb_ports_release(dev);
fail2:
- printk(KERN_ERR "fail2\n");
+ dev_err(&pdev->dev, "fail2\n");
ddb_buffers_free(dev);
fail1:
- printk(KERN_ERR "fail1\n");
+ dev_err(&pdev->dev, "fail1\n");
if (dev->msi)
pci_disable_msi(dev->pdev);
if (stat == 0)
free_irq(dev->pdev->irq, dev);
fail:
- printk(KERN_ERR "fail\n");
+ dev_err(&pdev->dev, "fail\n");
ddb_unmap(dev);
pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev);
@@ -1655,6 +2118,12 @@ static const struct ddb_info ddb_octopus_le = {
.port_num = 2,
};
+static const struct ddb_info ddb_octopus_oem = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Octopus OEM",
+ .port_num = 4,
+};
+
static const struct ddb_info ddb_octopus_mini = {
.type = DDB_OCTOPUS,
.name = "Digital Devices Octopus Mini",
@@ -1678,6 +2147,14 @@ static const struct ddb_info ddb_dvbct = {
.port_num = 3,
};
+static const struct ddb_info ddb_ctv7 = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Cine CT V7 DVB adapter",
+ .port_num = 4,
+ .board_control = 3,
+ .board_control_2 = 4,
+};
+
static const struct ddb_info ddb_satixS2v3 = {
.type = DDB_OCTOPUS,
.name = "Mystique SaTiX-S2 V3 DVB adapter",
@@ -1690,6 +2167,55 @@ static const struct ddb_info ddb_octopusv3 = {
.port_num = 4,
};
+/*** MaxA8 adapters ***********************************************************/
+
+static struct ddb_info ddb_ct2_8 = {
+ .type = DDB_OCTOPUS_MAX_CT,
+ .name = "Digital Devices MAX A8 CT2",
+ .port_num = 4,
+ .board_control = 0x0ff,
+ .board_control_2 = 0xf00,
+ .ts_quirks = TS_QUIRK_SERIAL,
+};
+
+static struct ddb_info ddb_c2t2_8 = {
+ .type = DDB_OCTOPUS_MAX_CT,
+ .name = "Digital Devices MAX A8 C2T2",
+ .port_num = 4,
+ .board_control = 0x0ff,
+ .board_control_2 = 0xf00,
+ .ts_quirks = TS_QUIRK_SERIAL,
+};
+
+static struct ddb_info ddb_isdbt_8 = {
+ .type = DDB_OCTOPUS_MAX_CT,
+ .name = "Digital Devices MAX A8 ISDBT",
+ .port_num = 4,
+ .board_control = 0x0ff,
+ .board_control_2 = 0xf00,
+ .ts_quirks = TS_QUIRK_SERIAL,
+};
+
+static struct ddb_info ddb_c2t2i_v0_8 = {
+ .type = DDB_OCTOPUS_MAX_CT,
+ .name = "Digital Devices MAX A8 C2T2I V0",
+ .port_num = 4,
+ .board_control = 0x0ff,
+ .board_control_2 = 0xf00,
+ .ts_quirks = TS_QUIRK_SERIAL | TS_QUIRK_ALT_OSC,
+};
+
+static struct ddb_info ddb_c2t2i_8 = {
+ .type = DDB_OCTOPUS_MAX_CT,
+ .name = "Digital Devices MAX A8 C2T2I",
+ .port_num = 4,
+ .board_control = 0x0ff,
+ .board_control_2 = 0xf00,
+ .ts_quirks = TS_QUIRK_SERIAL,
+};
+
+/******************************************************************************/
+
#define DDVID 0xdd01 /* Digital Devices Vendor ID */
#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \
@@ -1700,15 +2226,34 @@ static const struct ddb_info ddb_octopusv3 = {
static const struct pci_device_id ddb_id_tbl[] = {
DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
+ DDB_ID(DDVID, 0x0005, DDVID, 0x0004, ddb_octopusv3),
DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0003, ddb_octopus_oem),
DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus_mini),
+ DDB_ID(DDVID, 0x0005, DDVID, 0x0011, ddb_octopus_mini),
DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
DDB_ID(DDVID, 0x0003, DDVID, 0x0021, ddb_v6_5),
DDB_ID(DDVID, 0x0003, DDVID, 0x0030, ddb_dvbct),
DDB_ID(DDVID, 0x0003, DDVID, 0xdb03, ddb_satixS2v3),
- DDB_ID(DDVID, 0x0005, DDVID, 0x0004, ddb_octopusv3),
+ DDB_ID(DDVID, 0x0006, DDVID, 0x0031, ddb_ctv7),
+ DDB_ID(DDVID, 0x0006, DDVID, 0x0032, ddb_ctv7),
+ DDB_ID(DDVID, 0x0006, DDVID, 0x0033, ddb_ctv7),
+ DDB_ID(DDVID, 0x0008, DDVID, 0x0034, ddb_ct2_8),
+ DDB_ID(DDVID, 0x0008, DDVID, 0x0035, ddb_c2t2_8),
+ DDB_ID(DDVID, 0x0008, DDVID, 0x0036, ddb_isdbt_8),
+ DDB_ID(DDVID, 0x0008, DDVID, 0x0037, ddb_c2t2i_v0_8),
+ DDB_ID(DDVID, 0x0008, DDVID, 0x0038, ddb_c2t2i_8),
+ DDB_ID(DDVID, 0x0006, DDVID, 0x0039, ddb_ctv7),
/* in case sub-ids got deleted in flash */
DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0005, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0006, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0007, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0008, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0011, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0013, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0201, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ DDB_ID(DDVID, 0x0320, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
{0}
};
MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
@@ -1725,7 +2270,7 @@ static __init int module_init_ddbridge(void)
{
int ret;
- printk(KERN_INFO "Digital Devices PCIE bridge driver, Copyright (C) 2010-11 Digital Devices GmbH\n");
+ pr_info("Digital Devices PCIE bridge driver, Copyright (C) 2010-11 Digital Devices GmbH\n");
ret = ddb_class_create();
if (ret < 0)
diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h
index 6ae810324b4e..98cebb97d64f 100644
--- a/drivers/media/pci/ddbridge/ddbridge-regs.h
+++ b/drivers/media/pci/ddbridge/ddbridge-regs.h
@@ -34,6 +34,10 @@
/* ------------------------------------------------------------------------- */
+#define BOARD_CONTROL 0x30
+
+/* ------------------------------------------------------------------------- */
+
/* Interrupt controller */
/* How many MSI's are available depends on HW (Min 2 max 8) */
/* How many are usable also depends on Host platform */
diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h
index 185b423818d3..4a0e3283d646 100644
--- a/drivers/media/pci/ddbridge/ddbridge.h
+++ b/drivers/media/pci/ddbridge/ddbridge.h
@@ -43,14 +43,29 @@
#define DDB_MAX_PORT 4
#define DDB_MAX_INPUT 8
#define DDB_MAX_OUTPUT 4
+#define DDB_MAX_LINK 4
+#define DDB_LINK_SHIFT 28
+
+#define DDB_LINK_TAG(_x) (_x << DDB_LINK_SHIFT)
+
+#define DDB_XO2_TYPE_NONE 0
+#define DDB_XO2_TYPE_DUOFLEX 1
+#define DDB_XO2_TYPE_CI 2
struct ddb_info {
int type;
-#define DDB_NONE 0
-#define DDB_OCTOPUS 1
+#define DDB_NONE 0
+#define DDB_OCTOPUS 1
+#define DDB_OCTOPUS_MAX_CT 6
char *name;
int port_num;
u32 port_type[DDB_MAX_PORT];
+ u32 board_control;
+ u32 board_control_2;
+ u8 ts_quirks;
+#define TS_QUIRK_SERIAL 1
+#define TS_QUIRK_REVERSED 2
+#define TS_QUIRK_ALT_OSC 8
};
/* DMA_SIZE MUST be divisible by 188 and 128 !!! */
@@ -86,6 +101,7 @@ struct ddb_input {
struct dvb_adapter adap;
struct dvb_device *dev;
+ struct i2c_client *i2c_client[1];
struct dvb_frontend *fe;
struct dvb_frontend *fe2;
struct dmxdev dmxdev;
@@ -138,11 +154,22 @@ struct ddb_port {
#define DDB_PORT_CI 1
#define DDB_PORT_TUNER 2
u32 type;
-#define DDB_TUNER_NONE 0
-#define DDB_TUNER_DVBS_ST 1
-#define DDB_TUNER_DVBS_ST_AA 2
-#define DDB_TUNER_DVBCT_TR 16
-#define DDB_TUNER_DVBCT_ST 17
+#define DDB_TUNER_NONE 0
+#define DDB_TUNER_DVBS_ST 1
+#define DDB_TUNER_DVBS_ST_AA 2
+#define DDB_TUNER_DVBCT2_SONY_P 7
+#define DDB_TUNER_DVBC2T2_SONY_P 8
+#define DDB_TUNER_ISDBT_SONY_P 9
+#define DDB_TUNER_DVBC2T2I_SONY_P 15
+#define DDB_TUNER_DVBCT_TR 16
+#define DDB_TUNER_DVBCT_ST 17
+#define DDB_TUNER_XO2_DVBS_STV0910 32
+#define DDB_TUNER_XO2_DVBCT2_SONY 33
+#define DDB_TUNER_XO2_ISDBT_SONY 34
+#define DDB_TUNER_XO2_DVBC2T2_SONY 35
+#define DDB_TUNER_XO2_ATSC_ST 36
+#define DDB_TUNER_XO2_DVBC2T2I_SONY 37
+
u32 adr;
struct ddb_input *input[2];
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
index 807ead20d212..417d03da01f0 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
@@ -262,14 +262,16 @@ static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
unsigned long flags;
+ unsigned char *dma_area = NULL;
spin_lock_irqsave(&itvsc->slock, flags);
if (substream->runtime->dma_area) {
dprintk("freeing pcm capture region\n");
- vfree(substream->runtime->dma_area);
+ dma_area = substream->runtime->dma_area;
substream->runtime->dma_area = NULL;
}
spin_unlock_irqrestore(&itvsc->slock, flags);
+ vfree(dma_area);
return 0;
}
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
index 9444483fb942..5c0a4e614413 100644
--- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
@@ -122,7 +122,8 @@ static void netup_unidvb_queue_cleanup(struct netup_dma *dma);
static struct cxd2841er_config demod_config = {
.i2c_addr = 0xc8,
- .xtal = SONY_XTAL_24000
+ .xtal = SONY_XTAL_24000,
+ .flags = CXD2841ER_USE_GATECTRL | CXD2841ER_ASCOT
};
static struct horus3a_config horus3a_conf = {
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index ce69e648b663..8c92cb7f7e72 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -336,9 +336,9 @@ int ngene_command(struct ngene *dev, struct ngene_command *com)
{
int result;
- down(&dev->cmd_mutex);
+ mutex_lock(&dev->cmd_mutex);
result = ngene_command_mutex(dev, com);
- up(&dev->cmd_mutex);
+ mutex_unlock(&dev->cmd_mutex);
return result;
}
@@ -560,7 +560,6 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream,
u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
u16 BsSDO = 0x9B00;
- down(&dev->stream_mutex);
memset(&com, 0, sizeof(com));
com.cmd.hdr.Opcode = CMD_CONTROL;
com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
@@ -586,17 +585,13 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream,
chan->State = KSSTATE_ACQUIRE;
chan->HWState = HWSTATE_STOP;
spin_unlock_irq(&chan->state_lock);
- if (ngene_command(dev, &com) < 0) {
- up(&dev->stream_mutex);
+ if (ngene_command(dev, &com) < 0)
return -1;
- }
/* clear_buffers(chan); */
flush_buffers(chan);
- up(&dev->stream_mutex);
return 0;
}
spin_unlock_irq(&chan->state_lock);
- up(&dev->stream_mutex);
return 0;
}
@@ -692,11 +687,9 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream,
chan->HWState = HWSTATE_STARTUP;
spin_unlock_irq(&chan->state_lock);
- if (ngene_command(dev, &com) < 0) {
- up(&dev->stream_mutex);
+ if (ngene_command(dev, &com) < 0)
return -1;
- }
- up(&dev->stream_mutex);
+
return 0;
}
@@ -750,8 +743,11 @@ void set_transfer(struct ngene_channel *chan, int state)
/* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
ngreadl(0x9310)); */
+ mutex_lock(&dev->stream_mutex);
ret = ngene_command_stream_control(dev, chan->number,
control, mode, flags);
+ mutex_unlock(&dev->stream_mutex);
+
if (!ret)
chan->running = state;
else
@@ -1283,7 +1279,7 @@ static int ngene_load_firm(struct ngene *dev)
static void ngene_stop(struct ngene *dev)
{
- down(&dev->cmd_mutex);
+ mutex_destroy(&dev->cmd_mutex);
i2c_del_adapter(&(dev->channel[0].i2c_adapter));
i2c_del_adapter(&(dev->channel[1].i2c_adapter));
ngwritel(0, NGENE_INT_ENABLE);
@@ -1346,10 +1342,10 @@ static int ngene_start(struct ngene *dev)
init_waitqueue_head(&dev->cmd_wq);
init_waitqueue_head(&dev->tx_wq);
init_waitqueue_head(&dev->rx_wq);
- sema_init(&dev->cmd_mutex, 1);
- sema_init(&dev->stream_mutex, 1);
+ mutex_init(&dev->cmd_mutex);
+ mutex_init(&dev->stream_mutex);
sema_init(&dev->pll_mutex, 1);
- sema_init(&dev->i2c_switch_mutex, 1);
+ mutex_init(&dev->i2c_switch_mutex);
spin_lock_init(&dev->cmd_lock);
for (i = 0; i < MAX_STREAM; i++)
spin_lock_init(&dev->channel[i].state_lock);
@@ -1606,10 +1602,10 @@ static void ngene_unlink(struct ngene *dev)
com.in_len = 3;
com.out_len = 1;
- down(&dev->cmd_mutex);
+ mutex_lock(&dev->cmd_mutex);
ngwritel(0, NGENE_INT_ENABLE);
ngene_command_mutex(dev, &com);
- up(&dev->cmd_mutex);
+ mutex_unlock(&dev->cmd_mutex);
}
void ngene_shutdown(struct pci_dev *pdev)
diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c
index cf39fcf54adf..fbf36353c701 100644
--- a/drivers/media/pci/ngene/ngene-i2c.c
+++ b/drivers/media/pci/ngene/ngene-i2c.c
@@ -118,7 +118,7 @@ static int ngene_i2c_master_xfer(struct i2c_adapter *adapter,
(struct ngene_channel *)i2c_get_adapdata(adapter);
struct ngene *dev = chan->dev;
- down(&dev->i2c_switch_mutex);
+ mutex_lock(&dev->i2c_switch_mutex);
ngene_i2c_set_bus(dev, chan->number);
if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD))
@@ -136,11 +136,11 @@ static int ngene_i2c_master_xfer(struct i2c_adapter *adapter,
msg[0].buf, msg[0].len, 0))
goto done;
- up(&dev->i2c_switch_mutex);
+ mutex_unlock(&dev->i2c_switch_mutex);
return -EIO;
done:
- up(&dev->i2c_switch_mutex);
+ mutex_unlock(&dev->i2c_switch_mutex);
return num;
}
diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h
index 10d8f74c4f0a..7c7cd217333d 100644
--- a/drivers/media/pci/ngene/ngene.h
+++ b/drivers/media/pci/ngene/ngene.h
@@ -762,10 +762,10 @@ struct ngene {
wait_queue_head_t cmd_wq;
int cmd_done;
- struct semaphore cmd_mutex;
- struct semaphore stream_mutex;
+ struct mutex cmd_mutex;
+ struct mutex stream_mutex;
struct semaphore pll_mutex;
- struct semaphore i2c_switch_mutex;
+ struct mutex i2c_switch_mutex;
int i2c_current_channel;
int i2c_current_bus;
spinlock_t cmd_lock;
diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
index f79380faf499..9965d3531c80 100644
--- a/drivers/media/pci/saa7134/saa7134-cards.c
+++ b/drivers/media/pci/saa7134/saa7134-cards.c
@@ -7806,7 +7806,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
dev->name, saa7134_boards[dev->board].name);
break;
}
- /* break intentionally omitted */
+ /* fall-through */
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
@@ -7864,7 +7864,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
hauppauge_eeprom(dev, dev->eedata+0x80);
- /* break intentionally omitted */
+ /* fall-through */
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_KWORLD_DVBT_210:
case SAA7134_BOARD_TEVION_DVBT_220RF:
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index b2ff82fa7116..ecfeac5cdbed 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -389,11 +389,11 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
+ memcpy(msg, &msg_tmp, sizeof(*msg));
/* No need to update the read positions, because this was a peek */
/* If the caller specifically want to peek, return */
if (peekonly) {
- memcpy(msg, &msg_tmp, sizeof(*msg));
goto peekout;
}
@@ -438,21 +438,15 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
space_rem = bus->m_dwSizeGetRing - curr_grp;
if (space_rem < sizeof(*msg)) {
- /* msg wraps around the ring */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem);
- memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing,
- sizeof(*msg) - space_rem);
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
space_rem, buf_size);
} else if (space_rem == sizeof(*msg)) {
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
} else {
/* Additional data wraps around the ring */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf) {
memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
sizeof(*msg), space_rem - sizeof(*msg));
@@ -465,15 +459,10 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
} else {
/* No wrapping */
- memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
buf_size);
}
- /* Convert from little endian to CPU */
- msg->size = le16_to_cpu((__force __le16)msg->size);
- msg->command = le32_to_cpu((__force __le32)msg->command);
- msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
/* Update the read positions, adjusting the ring */
saa7164_writel(bus->m_dwGetReadPos, new_grp);
diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index 175015ca79f2..dfebd77ada59 100644
--- a/drivers/media/pci/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -506,6 +506,8 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
dprintk(DBGLVL_CMD,
"%s() UNKNOWN OR INVALID CONTROL\n",
__func__);
+ ret = SAA_ERR_NOT_SUPPORTED;
+ break;
default:
dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__);
ret = SAA_ERR_NOT_SUPPORTED;
diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c
index f50d07229236..ca0873e47bea 100644
--- a/drivers/media/pci/solo6x10/solo6x10-core.c
+++ b/drivers/media/pci/solo6x10/solo6x10-core.c
@@ -511,6 +511,7 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
default:
dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n",
chip_id);
+ /* fall through */
case 5:
solo_dev->nr_chans = 4;
solo_dev->nr_ext = 1;
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c
index 36e93540bb49..3ca947092775 100644
--- a/drivers/media/pci/solo6x10/solo6x10-g723.c
+++ b/drivers/media/pci/solo6x10/solo6x10-g723.c
@@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
return idx * G723_FRAMES_PER_PAGE;
}
-static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
- snd_pcm_uframes_t pos, void __user *dst,
- snd_pcm_uframes_t count)
+static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss,
+ unsigned long pos, void *dst,
+ unsigned long count, bool in_kernel)
{
struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
struct solo_dev *solo_dev = solo_pcm->solo_dev;
@@ -242,16 +242,31 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
if (err)
return err;
- err = copy_to_user(dst + (i * G723_PERIOD_BYTES),
- solo_pcm->g723_buf, G723_PERIOD_BYTES);
-
- if (err)
+ if (in_kernel)
+ memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES);
+ else if (copy_to_user((void __user *)dst,
+ solo_pcm->g723_buf, G723_PERIOD_BYTES))
return -EFAULT;
+ dst += G723_PERIOD_BYTES;
}
return 0;
}
+static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
+ unsigned long pos, void __user *dst,
+ unsigned long count)
+{
+ return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false);
+}
+
+static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel,
+ unsigned long pos, void *dst,
+ unsigned long count)
+{
+ return __snd_solo_pcm_copy(ss, pos, dst, count, true);
+}
+
static const struct snd_pcm_ops snd_solo_pcm_ops = {
.open = snd_solo_pcm_open,
.close = snd_solo_pcm_close,
@@ -261,7 +276,8 @@ static const struct snd_pcm_ops snd_solo_pcm_ops = {
.prepare = snd_solo_pcm_prepare,
.trigger = snd_solo_pcm_trigger,
.pointer = snd_solo_pcm_pointer,
- .copy = snd_solo_pcm_copy,
+ .copy_user = snd_solo_pcm_copy_user,
+ .copy_kernel = snd_solo_pcm_copy_kernel,
};
static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol,
diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c
index e83bb79f9349..89f2f2a493c2 100644
--- a/drivers/media/pci/solo6x10/solo6x10-i2c.c
+++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c
@@ -192,6 +192,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev)
}
solo_dev->i2c_state = IIC_STATE_WRITE;
+ /* fall through */
case IIC_STATE_WRITE:
ret = solo_i2c_handle_write(solo_dev);
break;
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index df9395c87178..f2905bd80366 100644
--- a/drivers/media/pci/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -336,6 +336,7 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len,
av7110_p2t_write(buffer1, buffer1_len,
dvbdmxfilter->feed->pid,
&av7110->p2t_filter[dvbdmxfilter->index]);
+ return 0;
default:
return 0;
}
@@ -451,8 +452,12 @@ static void debiirq(unsigned long cookie)
case DATA_CI_PUT:
dprintk(4, "debi DATA_CI_PUT\n");
+ xfer = TX_BUFF;
+ break;
case DATA_MPEG_PLAY:
dprintk(4, "debi DATA_MPEG_PLAY\n");
+ xfer = TX_BUFF;
+ break;
case DATA_BMP_LOAD:
dprintk(4, "debi DATA_BMP_LOAD\n");
xfer = TX_BUFF;
diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c
index 2a044be729da..e7bd2b8484e3 100644
--- a/drivers/media/pci/tw5864/tw5864-video.c
+++ b/drivers/media/pci/tw5864/tw5864-video.c
@@ -545,6 +545,7 @@ static int tw5864_fmt_vid_cap(struct file *file, void *priv,
switch (input->std) {
default:
WARN_ON_ONCE(1);
+ return -EINVAL;
case STD_NTSC:
f->fmt.pix.height = 480;
break;
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
index 180f3d7af3e1..a11cb501c550 100644
--- a/drivers/media/pci/zoran/zoran_driver.c
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -534,6 +534,7 @@ static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
KERN_WARNING
"%s: %s - queueing buffer %d in state DONE!?\n",
ZR_DEVNAME(zr), __func__, num);
+ /* fall through */
case BUZ_STATE_USER:
/* since there is at least one unused buffer there's room for at least
* one more pend[] entry */
@@ -693,6 +694,7 @@ static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
KERN_WARNING
"%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
ZR_DEVNAME(zr), __func__);
+ /* fall through */
case BUZ_STATE_USER:
/* since there is at least one unused buffer there's room for at
*least one more pend[] entry */
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 041cb80a26b1..fb1fa0b82077 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -74,6 +74,13 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
+config VIDEO_MUX
+ tristate "Video Multiplexer"
+ depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+ select REGMAP
+ help
+ This driver provides support for N:1 video bus multiplexers.
+
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
@@ -82,6 +89,7 @@ config VIDEO_OMAP3
select ARM_DMA_USE_IOMMU
select VIDEOBUF2_DMA_CONTIG
select MFD_SYSCON
+ select V4L2_FWNODE
---help---
Driver for an OMAP 3 camera controller.
@@ -97,6 +105,7 @@ config VIDEO_PXA27x
depends on PXA27x || COMPILE_TEST
select VIDEOBUF2_DMA_SG
select SG_SPLIT
+ select V4L2_FWNODE
---help---
This is a v4l2 driver for the PXA27x Quick Capture Interface
@@ -114,6 +123,19 @@ config VIDEO_S3C_CAMIF
To compile this driver as a module, choose M here: the module
will be called s3c-camif.
+config VIDEO_STM32_DCMI
+ tristate "STM32 Digital Camera Memory Interface (DCMI) support"
+ depends on VIDEO_V4L2 && OF && HAS_DMA
+ depends on ARCH_STM32 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ ---help---
+ This module makes the STM32 Digital Camera Memory Interface (DCMI)
+ available as a v4l2 device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called stm32-dcmi.
+
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
@@ -127,6 +149,7 @@ config VIDEO_TI_CAL
depends on SOC_DRA7XX || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
default n
---help---
Support for the TI CAL (Camera Adaptation Layer) block
@@ -448,6 +471,20 @@ config VIDEO_TI_VPE_DEBUG
---help---
Enable debug messages on VPE driver.
+config VIDEO_QCOM_VENUS
+ tristate "Qualcomm Venus V4L2 encoder/decoder driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ select QCOM_MDT_LOADER if ARCH_QCOM
+ select QCOM_SCM if ARCH_QCOM
+ select VIDEOBUF2_DMA_SG
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ This is a V4L2 driver for Qualcomm Venus video accelerator
+ hardware. It accelerates encoding and decoding operations
+ on various Qualcomm SoCs.
+ To compile this driver as a module choose m here.
+
endif # V4L_MEM2MEM_DRIVERS
# TI VIDEO PORT Helper Modules
@@ -521,4 +558,41 @@ config VIDEO_STI_HDMI_CEC
CEC bus is present in the HDMI connector and enables communication
between compatible devices.
+config VIDEO_STM32_HDMI_CEC
+ tristate "STMicroelectronics STM32 HDMI CEC driver"
+ depends on ARCH_STM32 || COMPILE_TEST
+ select REGMAP
+ select REGMAP_MMIO
+ select CEC_CORE
+ ---help---
+ This is a driver for STM32 interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
endif #CEC_PLATFORM_DRIVERS
+
+menuconfig SDR_PLATFORM_DRIVERS
+ bool "SDR platform devices"
+ depends on MEDIA_SDR_SUPPORT
+ default n
+ ---help---
+ Say Y here to enable support for platform-specific SDR Drivers.
+
+if SDR_PLATFORM_DRIVERS
+
+config VIDEO_RCAR_DRIF
+ tristate "Renesas Digitial Radio Interface (DRIF)"
+ depends on VIDEO_V4L2 && HAS_DMA
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select VIDEOBUF2_VMALLOC
+ ---help---
+ Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
+ Radio Interface that interfaces with an RF front end chip. It is a
+ receiver of digital data which uses DMA to transfer received data to
+ a configured location for an application to use.
+
+ To compile this driver as a module, choose M here; the module
+ will be called rcar_drif.
+
+endif # SDR_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 63303d63c64c..9beadc760467 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -28,6 +28,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
+obj-$(CONFIG_VIDEO_MUX) += video-mux.o
+
obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
@@ -44,14 +46,17 @@ obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += sti/cec/
obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/
-obj-$(CONFIG_BLACKFIN) += blackfin/
+obj-y += stm32/
+
+obj-y += blackfin/
-obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+obj-y += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
@@ -68,6 +73,8 @@ obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/
obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
+obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
+
ccflags-y += -I$(srctree)/drivers/media/i2c
obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
@@ -77,3 +84,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/
obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/
obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/
+
+obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig
index 42d9c186710a..160e77e9a0fb 100644
--- a/drivers/media/platform/am437x/Kconfig
+++ b/drivers/media/platform/am437x/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_AM437X_VPFE
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
depends on SOC_AM43XX || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
help
Support for AM437x Video Processing Front End based Video
Capture Driver.
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index 05489a401c5c..466aba8b0e00 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -36,7 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include "am437x-vpfe.h"
@@ -2303,7 +2304,8 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier,
vpfe_dbg(1, vpfe, "vpfe_async_bound\n");
for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) {
- if (vpfe->cfg->asd[i]->match.of.node == asd[i].match.of.node) {
+ if (vpfe->cfg->asd[i]->match.fwnode.fwnode ==
+ asd[i].match.fwnode.fwnode) {
sdinfo = &vpfe->cfg->sub_devs[i];
vpfe->sd[i] = subdev;
vpfe->sd[i]->grp_id = sdinfo->grp_id;
@@ -2419,7 +2421,7 @@ static struct vpfe_config *
vpfe_get_pdata(struct platform_device *pdev)
{
struct device_node *endpoint = NULL;
- struct v4l2_of_endpoint bus_cfg;
+ struct v4l2_fwnode_endpoint bus_cfg;
struct vpfe_subdev_info *sdinfo;
struct vpfe_config *pdata;
unsigned int flags;
@@ -2463,7 +2465,8 @@ vpfe_get_pdata(struct platform_device *pdev)
sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER;
}
- err = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+ &bus_cfg);
if (err) {
dev_err(&pdev->dev, "Could not parse the endpoint\n");
goto done;
@@ -2501,8 +2504,8 @@ vpfe_get_pdata(struct platform_device *pdev)
goto done;
}
- pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF;
- pdata->asd[i]->match.of.node = rem;
+ pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ pdata->asd[i]->match.fwnode.fwnode = of_fwnode_handle(rem);
of_node_put(rem);
}
diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig
index 9bd0f19b127f..55de751e5f51 100644
--- a/drivers/media/platform/atmel/Kconfig
+++ b/drivers/media/platform/atmel/Kconfig
@@ -4,6 +4,7 @@ config VIDEO_ATMEL_ISC
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select REGMAP_MMIO
+ select V4L2_FWNODE
help
This module makes the ATMEL Image Sensor Controller available
as a v4l2 device.
@@ -13,6 +14,7 @@ config VIDEO_ATMEL_ISI
depends on VIDEO_V4L2 && OF && HAS_DMA
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
---help---
This module makes the ATMEL Image Sensor Interface available
as a v4l2 device.
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index c4b2115559a5..d6534252cdcd 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -32,6 +32,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -42,7 +43,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
@@ -239,13 +240,11 @@ static struct isc_format isc_formats[] = {
{ V4L2_PIX_FMT_YUV420, 0x0, 12,
ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
- ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
- ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
+ ISC_DCFG_IMODE_YC420P, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
false, false },
{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
- ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
- ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
+ ISC_DCFG_IMODE_YC422P, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
false, false },
{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
@@ -700,8 +699,10 @@ static void isc_set_histogram(struct isc_device *isc)
}
static inline void isc_get_param(const struct isc_format *fmt,
- u32 *rlp_mode, u32 *dcfg_imode)
+ u32 *rlp_mode, u32 *dcfg)
{
+ *dcfg = ISC_DCFG_YMBSIZE_BEATS8;
+
switch (fmt->fourcc) {
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
@@ -712,11 +713,11 @@ static inline void isc_get_param(const struct isc_format *fmt,
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
*rlp_mode = fmt->reg_rlp_mode;
- *dcfg_imode = fmt->reg_dcfg_imode;
+ *dcfg |= fmt->reg_dcfg_imode;
break;
default:
*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
- *dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+ *dcfg |= ISC_DCFG_IMODE_PACKED8;
break;
}
}
@@ -726,18 +727,19 @@ static int isc_configure(struct isc_device *isc)
struct regmap *regmap = isc->regmap;
const struct isc_format *current_fmt = isc->current_fmt;
struct isc_subdev_entity *subdev = isc->current_subdev;
- u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
+ u32 pfe_cfg0, rlp_mode, dcfg, mask, pipeline;
if (sensor_is_preferred(current_fmt)) {
pfe_cfg0 = current_fmt->reg_bps;
pipeline = 0x0;
- isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
+ isc_get_param(current_fmt, &rlp_mode, &dcfg);
isc->ctrls.hist_stat = HIST_INIT;
} else {
pfe_cfg0 = isc->raw_fmt->reg_bps;
pipeline = current_fmt->pipeline;
rlp_mode = current_fmt->reg_rlp_mode;
- dcfg_imode = current_fmt->reg_dcfg_imode;
+ dcfg = current_fmt->reg_dcfg_imode | ISC_DCFG_YMBSIZE_BEATS8 |
+ ISC_DCFG_CMBSIZE_BEATS8;
}
pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
@@ -750,7 +752,7 @@ static int isc_configure(struct isc_device *isc)
regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
rlp_mode);
- regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
+ regmap_write(regmap, ISC_DCFG, dcfg);
/* Set the pipeline */
isc_set_pipeline(isc, pipeline);
@@ -1684,7 +1686,7 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
{
struct device_node *np = dev->of_node;
struct device_node *epn = NULL, *rem;
- struct v4l2_of_endpoint v4l2_epn;
+ struct v4l2_fwnode_endpoint v4l2_epn;
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
int ret;
@@ -1703,7 +1705,8 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
continue;
}
- ret = v4l2_of_parse_endpoint(epn, &v4l2_epn);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
+ &v4l2_epn);
if (ret) {
of_node_put(rem);
ret = -EINVAL;
@@ -1738,8 +1741,9 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
- subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_OF;
- subdev_entity->asd->match.of.node = rem;
+ subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ subdev_entity->asd->match.fwnode.fwnode =
+ of_fwnode_handle(rem);
list_add_tail(&subdev_entity->list, &isc->subdev_entities);
}
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index e4867f84514c..891fa2505efa 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -30,14 +31,14 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-image-sizes.h>
#include "atmel-isi.h"
-#define MAX_SUPPORT_WIDTH 2048
-#define MAX_SUPPORT_HEIGHT 2048
+#define MAX_SUPPORT_WIDTH 2048U
+#define MAX_SUPPORT_HEIGHT 2048U
#define MIN_FRAME_RATE 15
#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
@@ -424,6 +425,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct frame_buffer *buf, *node;
int ret;
+ pm_runtime_get_sync(isi->dev);
+
/* Enable stream on the sub device */
ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD) {
@@ -431,8 +434,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
goto err_start_stream;
}
- pm_runtime_get_sync(isi->dev);
-
/* Reset ISI */
ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
if (ret < 0) {
@@ -455,10 +456,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
return 0;
err_reset:
- pm_runtime_put(isi->dev);
v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0);
err_start_stream:
+ pm_runtime_put(isi->dev);
+
spin_lock_irq(&isi->irqlock);
isi->active = NULL;
/* Release all active buffers */
@@ -566,20 +568,15 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f,
};
int ret;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat);
if (!isi_fmt) {
isi_fmt = isi->user_formats[isi->num_user_formats - 1];
pixfmt->pixelformat = isi_fmt->fourcc;
}
- /* Limit to Atmel ISC hardware capabilities */
- if (pixfmt->width > MAX_SUPPORT_WIDTH)
- pixfmt->width = MAX_SUPPORT_WIDTH;
- if (pixfmt->height > MAX_SUPPORT_HEIGHT)
- pixfmt->height = MAX_SUPPORT_HEIGHT;
+ /* Limit to Atmel ISI hardware capabilities */
+ pixfmt->width = clamp(pixfmt->width, 0U, MAX_SUPPORT_WIDTH);
+ pixfmt->height = clamp(pixfmt->height, 0U, MAX_SUPPORT_HEIGHT);
v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code);
ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt,
@@ -801,7 +798,7 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct v4l2_of_endpoint ep;
+ struct v4l2_fwnode_endpoint ep;
int err;
/* Default settings for ISI */
@@ -814,7 +811,7 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi,
return -EINVAL;
}
- err = v4l2_of_parse_endpoint(np, &ep);
+ err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep);
of_node_put(np);
if (err) {
dev_err(&pdev->dev, "Could not parse the endpoint\n");
@@ -1058,7 +1055,7 @@ static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier)
struct atmel_isi *isi = notifier_to_isi(notifier);
int ret;
- isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler;
+ isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler;
ret = isi_formats_init(isi);
if (ret) {
dev_err(isi->dev, "No supported mediabus format found\n");
@@ -1126,8 +1123,8 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node)
/* Remote node to connect */
isi->entity.node = remote;
- isi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF;
- isi->entity.asd.match.of.node = remote;
+ isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ isi->entity.asd.match.fwnode.fwnode = of_fwnode_handle(remote);
return 0;
}
}
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 403214e00e95..bba1eb43b5d8 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -393,8 +393,8 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
int ret;
int i;
- if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
- ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) {
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 ||
+ ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264) {
width = round_up(q_data->width, 16);
height = round_up(q_data->height, 16);
} else {
@@ -427,14 +427,16 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
/* Register frame buffers in the parameter buffer */
for (i = 0; i < ctx->num_internal_frames; i++) {
- u32 y, cb, cr;
+ u32 y, cb, cr, mvcol;
/* Start addresses of Y, Cb, Cr planes */
y = ctx->internal_frames[i].paddr;
cb = y + ysize;
cr = y + ysize + ysize/4;
+ mvcol = y + ysize + ysize/4 + ysize/4;
if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) {
cb = round_up(cb, 4096);
+ mvcol = cb + ysize/2;
cr = 0;
/* Packed 20-bit MSB of base addresses */
/* YYYYYCCC, CCyyyyyc, cccc.... */
@@ -448,9 +450,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
/* mvcol buffer for h.264 */
if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
dev->devtype->product != CODA_DX6)
- coda_parabuf_write(ctx, 96 + i,
- ctx->internal_frames[i].paddr +
- ysize + ysize/4 + ysize/4);
+ coda_parabuf_write(ctx, 96 + i, mvcol);
}
/* mvcol buffer for mpeg4 */
@@ -1247,12 +1247,18 @@ static int coda_prepare_encode(struct coda_ctx *ctx)
dst_buf->sequence = ctx->osequence;
ctx->osequence++;
+ force_ipicture = ctx->params.force_ipicture;
+ if (force_ipicture)
+ ctx->params.force_ipicture = false;
+ else if ((src_buf->sequence % ctx->params.gop_size) == 0)
+ force_ipicture = 1;
+
/*
* Workaround coda firmware BUG that only marks the first
* frame as IDR. This is a problem for some decoders that can't
* recover when a frame is lost.
*/
- if (src_buf->sequence % ctx->params.gop_size) {
+ if (!force_ipicture) {
src_buf->flags |= V4L2_BUF_FLAG_PFRAME;
src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME;
} else {
@@ -1264,10 +1270,10 @@ static int coda_prepare_encode(struct coda_ctx *ctx)
coda_set_gdi_regs(ctx);
/*
- * Copy headers at the beginning of the first frame for H.264 only.
- * In MPEG4 they are already copied by the coda.
+ * Copy headers in front of the first frame and forced I frames for
+ * H.264 only. In MPEG4 they are already copied by the CODA.
*/
- if (src_buf->sequence == 0) {
+ if (src_buf->sequence == 0 || force_ipicture) {
pic_stream_buffer_addr =
vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) +
ctx->vpu_header_size[0] +
@@ -1291,8 +1297,7 @@ static int coda_prepare_encode(struct coda_ctx *ctx)
pic_stream_buffer_size = q_data_dst->sizeimage;
}
- if (src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) {
- force_ipicture = 1;
+ if (force_ipicture) {
switch (dst_fourcc) {
case V4L2_PIX_FMT_H264:
quant_param = ctx->params.h264_intra_qp;
@@ -1309,7 +1314,6 @@ static int coda_prepare_encode(struct coda_ctx *ctx)
break;
}
} else {
- force_ipicture = 0;
switch (dst_fourcc) {
case V4L2_PIX_FMT_H264:
quant_param = ctx->params.h264_inter_qp;
@@ -1382,7 +1386,8 @@ static void coda_finish_encode(struct coda_ctx *ctx)
wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
/* Calculate bytesused field */
- if (dst_buf->sequence == 0) {
+ if (dst_buf->sequence == 0 ||
+ src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) {
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr +
ctx->vpu_header_size[0] +
ctx->vpu_header_size[1] +
@@ -2193,12 +2198,32 @@ static void coda_finish_decode(struct coda_ctx *ctx)
ctx->display_idx = display_idx;
}
+static void coda_decode_timeout(struct coda_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *dst_buf;
+
+ /*
+ * For now this only handles the case where we would deadlock with
+ * userspace, i.e. userspace issued DEC_CMD_STOP and waits for EOS,
+ * but after a failed decode run we would hold the context and wait for
+ * userspace to queue more buffers.
+ */
+ if (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))
+ return;
+
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf->sequence = ctx->qsequence - 1;
+
+ coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR);
+}
+
const struct coda_context_ops coda_bit_decode_ops = {
.queue_init = coda_decoder_queue_init,
.reqbufs = coda_decoder_reqbufs,
.start_streaming = coda_start_decoding,
.prepare_run = coda_prepare_decode,
.finish_run = coda_finish_decode,
+ .run_timeout = coda_decode_timeout,
.seq_end_work = coda_seq_end_work,
.release = coda_bit_release,
};
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index d523e990d509..829c7895a98a 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -430,10 +430,10 @@ static int coda_g_fmt(struct file *file, void *priv,
f->fmt.pix.bytesperline = q_data->bytesperline;
f->fmt.pix.sizeimage = q_data->sizeimage;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
- else
- f->fmt.pix.colorspace = ctx->colorspace;
+ f->fmt.pix.colorspace = ctx->colorspace;
+ f->fmt.pix.xfer_func = ctx->xfer_func;
+ f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
+ f->fmt.pix.quantization = ctx->quantization;
return 0;
}
@@ -599,6 +599,9 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
}
f->fmt.pix.colorspace = ctx->colorspace;
+ f->fmt.pix.xfer_func = ctx->xfer_func;
+ f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
+ f->fmt.pix.quantization = ctx->quantization;
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
@@ -612,7 +615,6 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
/* The h.264 decoder only returns complete 16x16 macroblocks */
if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
- f->fmt.pix.width = f->fmt.pix.width;
f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
@@ -635,6 +637,23 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static void coda_set_default_colorspace(struct v4l2_pix_format *fmt)
+{
+ enum v4l2_colorspace colorspace;
+
+ if (fmt->pixelformat == V4L2_PIX_FMT_JPEG)
+ colorspace = V4L2_COLORSPACE_JPEG;
+ else if (fmt->width <= 720 && fmt->height <= 576)
+ colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ colorspace = V4L2_COLORSPACE_REC709;
+
+ fmt->colorspace = colorspace;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+}
+
static int coda_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -648,16 +667,8 @@ static int coda_try_fmt_vid_out(struct file *file, void *priv,
if (ret < 0)
return ret;
- switch (f->fmt.pix.colorspace) {
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_JPEG:
- break;
- default:
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
- else
- f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
- }
+ if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT)
+ coda_set_default_colorspace(&f->fmt.pix);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc);
@@ -772,6 +783,9 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv,
return ret;
ctx->colorspace = f->fmt.pix.colorspace;
+ ctx->xfer_func = f->fmt.pix.xfer_func;
+ ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->quantization = f->fmt.pix.quantization;
memset(&f_cap, 0, sizeof(f_cap));
f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1149,6 +1163,9 @@ static void coda_pic_run_work(struct work_struct *work)
ctx->hold = true;
coda_hw_reset(ctx);
+
+ if (ctx->ops->run_timeout)
+ ctx->ops->run_timeout(ctx);
} else if (!ctx->aborting) {
ctx->ops->finish_run(ctx);
}
@@ -1282,7 +1299,13 @@ static void set_default_params(struct coda_ctx *ctx)
csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h);
ctx->params.codec_mode = ctx->codec->mode;
- ctx->colorspace = V4L2_COLORSPACE_REC709;
+ if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_JPEG)
+ ctx->colorspace = V4L2_COLORSPACE_JPEG;
+ else
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
ctx->params.framerate = 30;
/* Default formats for output and input queues */
@@ -1680,6 +1703,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
ctx->params.intra_refresh = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ ctx->params.force_ipicture = true;
+ break;
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
coda_set_jpeg_compression_quality(ctx, ctrl->val);
break;
@@ -2063,8 +2089,7 @@ static int coda_hw_init(struct coda_dev *dev)
if (ret)
goto err_clk_ahb;
- if (dev->rstc)
- reset_control_reset(dev->rstc);
+ reset_control_reset(dev->rstc);
/*
* Copy the first CODA_ISRAM_SIZE in the internal SRAM.
@@ -2448,13 +2473,8 @@ static int coda_probe(struct platform_device *pdev)
dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
if (IS_ERR(dev->rstc)) {
ret = PTR_ERR(dev->rstc);
- if (ret == -ENOENT || ret == -ENOTSUPP) {
- dev->rstc = NULL;
- } else {
- dev_err(&pdev->dev, "failed get reset control: %d\n",
- ret);
- return ret;
- }
+ dev_err(&pdev->dev, "failed get reset control: %d\n", ret);
+ return ret;
}
/* Get IRAM pool from device tree or platform data */
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 20222befb9b2..c5f504d8cf67 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -135,6 +135,7 @@ struct coda_params {
u32 vbv_size;
u32 slice_max_bits;
u32 slice_max_mb;
+ bool force_ipicture;
};
struct coda_buffer_meta {
@@ -182,6 +183,7 @@ struct coda_context_ops {
int (*start_streaming)(struct coda_ctx *ctx);
int (*prepare_run)(struct coda_ctx *ctx);
void (*finish_run)(struct coda_ctx *ctx);
+ void (*run_timeout)(struct coda_ctx *ctx);
void (*seq_end_work)(struct work_struct *work);
void (*release)(struct coda_ctx *ctx);
};
@@ -206,6 +208,9 @@ struct coda_ctx {
enum coda_inst_type inst_type;
const struct coda_codec *codec;
enum v4l2_colorspace colorspace;
+ enum v4l2_xfer_func xfer_func;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
struct coda_params params;
struct v4l2_ctrl_handler ctrls;
struct v4l2_fh fh;
diff --git a/drivers/media/platform/coda/imx-vdoa.c b/drivers/media/platform/coda/imx-vdoa.c
index 669a4c82f1ff..df9b71621420 100644
--- a/drivers/media/platform/coda/imx-vdoa.c
+++ b/drivers/media/platform/coda/imx-vdoa.c
@@ -101,6 +101,8 @@ struct vdoa_ctx {
struct vdoa_data *vdoa;
struct completion completion;
struct vdoa_q_data q_data[2];
+ unsigned int submitted_job;
+ unsigned int completed_job;
};
static irqreturn_t vdoa_irq_handler(int irq, void *data)
@@ -114,7 +116,7 @@ static irqreturn_t vdoa_irq_handler(int irq, void *data)
curr_ctx = vdoa->curr_ctx;
if (!curr_ctx) {
- dev_dbg(vdoa->dev,
+ dev_warn(vdoa->dev,
"Instance released before the end of transaction\n");
return IRQ_HANDLED;
}
@@ -127,19 +129,44 @@ static irqreturn_t vdoa_irq_handler(int irq, void *data)
} else if (!(val & VDOAIST_EOT)) {
dev_warn(vdoa->dev, "Spurious interrupt\n");
}
+ curr_ctx->completed_job++;
complete(&curr_ctx->completion);
return IRQ_HANDLED;
}
+int vdoa_wait_for_completion(struct vdoa_ctx *ctx)
+{
+ struct vdoa_data *vdoa = ctx->vdoa;
+
+ if (ctx->submitted_job == ctx->completed_job)
+ return 0;
+
+ if (!wait_for_completion_timeout(&ctx->completion,
+ msecs_to_jiffies(300))) {
+ dev_err(vdoa->dev,
+ "Timeout waiting for transfer result\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(vdoa_wait_for_completion);
+
void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src)
{
struct vdoa_q_data *src_q_data, *dst_q_data;
struct vdoa_data *vdoa = ctx->vdoa;
u32 val;
+ if (vdoa->curr_ctx)
+ vdoa_wait_for_completion(vdoa->curr_ctx);
+
vdoa->curr_ctx = ctx;
+ reinit_completion(&ctx->completion);
+ ctx->submitted_job++;
+
src_q_data = &ctx->q_data[V4L2_M2M_SRC];
dst_q_data = &ctx->q_data[V4L2_M2M_DST];
@@ -177,21 +204,6 @@ void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src)
}
EXPORT_SYMBOL(vdoa_device_run);
-int vdoa_wait_for_completion(struct vdoa_ctx *ctx)
-{
- struct vdoa_data *vdoa = ctx->vdoa;
-
- if (!wait_for_completion_timeout(&ctx->completion,
- msecs_to_jiffies(300))) {
- dev_err(vdoa->dev,
- "Timeout waiting for transfer result\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(vdoa_wait_for_completion);
-
struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa)
{
struct vdoa_ctx *ctx;
@@ -218,6 +230,11 @@ void vdoa_context_destroy(struct vdoa_ctx *ctx)
{
struct vdoa_data *vdoa = ctx->vdoa;
+ if (vdoa->curr_ctx == ctx) {
+ vdoa_wait_for_completion(vdoa->curr_ctx);
+ vdoa->curr_ctx = NULL;
+ }
+
clk_disable_unprepare(vdoa->vdoa_clk);
kfree(ctx);
}
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 554e710de487..55982e681d77 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -22,6 +22,7 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
depends on HAS_DMA
depends on I2C
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
help
Enables Davinci VPIF module used for capture devices.
This module is used for capture on TI DM6467/DA850/OMAPL138
diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/davinci/ccdc_hw_device.h
index 8f6688a7a111..f1b521045d64 100644
--- a/drivers/media/platform/davinci/ccdc_hw_device.h
+++ b/drivers/media/platform/davinci/ccdc_hw_device.h
@@ -42,16 +42,6 @@ struct ccdc_hw_ops {
int (*set_hw_if_params) (struct vpfe_hw_if_param *param);
/* get interface parameters */
int (*get_hw_if_params) (struct vpfe_hw_if_param *param);
- /*
- * Pointer to function to set parameters. Used
- * for implementing VPFE_S_CCDC_PARAMS
- */
- int (*set_params) (void *params);
- /*
- * Pointer to function to get parameter. Used
- * for implementing VPFE_G_CCDC_PARAMS
- */
- int (*get_params) (void *params);
/* Pointer to function to configure ccdc */
int (*configure) (void);
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index 73db166dc338..6d492dc4c3a9 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -17,12 +17,7 @@
* This module is for configuring DM355 CCD controller of VPFE to capture
* Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
* such as Defect Pixel Correction, Color Space Conversion etc to
- * pre-process the Bayer RGB data, before writing it to SDRAM. This
- * module also allows application to configure individual
- * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
- * To do so, application include dm355_ccdc.h and vpfe_capture.h header
- * files. The setparams() API is called by vpfe_capture driver
- * to configure module parameters
+ * pre-process the Bayer RGB data, before writing it to SDRAM.
*
* TODO: 1) Raw bayer parameter settings and bayer capture
* 2) Split module parameter structure to module specific ioctl structs
@@ -260,90 +255,6 @@ static void ccdc_setwin(struct v4l2_rect *image_win,
dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
}
-static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
-{
- if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
- ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
- return -EINVAL;
- }
-
- if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
- ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
- return -EINVAL;
- }
-
- if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
- ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
- return -EINVAL;
- }
-
- if ((ccdcparam->med_filt_thres < 0) ||
- (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of median filter threshold\n");
- return -EINVAL;
- }
-
- if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
- ccdcparam->data_sz > CCDC_DATA_8BITS) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
- return -EINVAL;
- }
-
- if (ccdcparam->alaw.enable) {
- if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 ||
- ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
- return -EINVAL;
- }
- }
-
- if (ccdcparam->blk_clamp.b_clamp_enable) {
- if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
- ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of sample pixel\n");
- return -EINVAL;
- }
- if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
- ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of sample lines\n");
- return -EINVAL;
- }
- }
- return 0;
-}
-
-/* Parameter operations */
-static int ccdc_set_params(void __user *params)
-{
- struct ccdc_config_params_raw ccdc_raw_params;
- int x;
-
- /* only raw module parameters can be set through the IOCTL */
- if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
- return -EINVAL;
-
- x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
- if (x) {
- dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdcparams, %d\n",
- x);
- return -EFAULT;
- }
-
- if (!validate_ccdc_param(&ccdc_raw_params)) {
- memcpy(&ccdc_cfg.bayer.config_params,
- &ccdc_raw_params,
- sizeof(ccdc_raw_params));
- return 0;
- }
- return -EINVAL;
-}
-
/* This function will configure CCDC for YCbCr video capture */
static void ccdc_config_ycbcr(void)
{
@@ -939,7 +850,6 @@ static struct ccdc_hw_device ccdc_hw_dev = {
.enable = ccdc_enable,
.enable_out_to_sdram = ccdc_enable_output_to_sdram,
.set_hw_if_params = ccdc_set_hw_if_params,
- .set_params = ccdc_set_params,
.configure = ccdc_configure,
.set_buftype = ccdc_set_buftype,
.get_buftype = ccdc_get_buftype,
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index 740fbc7a8c14..3b2d8a9317b8 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -17,13 +17,9 @@
* This module is for configuring CCD controller of DM6446 VPFE to capture
* Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
* such as Defect Pixel Correction, Color Space Conversion etc to
- * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This
- * module also allows application to configure individual
- * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
- * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header
- * files. The setparams() API is called by vpfe_capture driver
- * to configure module parameters. This file is named DM644x so that other
- * variants such DM6443 may be supported using the same module.
+ * pre-process the Raw Bayer RGB data, before writing it to SDRAM.
+ * This file is named DM644x so that other variants such DM6443
+ * may be supported using the same module.
*
* TODO: Test Raw bayer parameter settings and bayer capture
* Split module parameter structure to module specific ioctl structs
@@ -216,96 +212,8 @@ static void ccdc_readregs(void)
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
}
-static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
-{
- if (ccdcparam->alaw.enable) {
- u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd);
- u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);
-
- if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) ||
- (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) ||
- (max_gamma > max_data)) {
- dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
- return -1;
- }
- }
- return 0;
-}
-
-static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
-{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int *fpc_virtaddr = NULL;
- unsigned int *fpc_physaddr = NULL;
-
- memcpy(config_params, raw_params, sizeof(*raw_params));
- /*
- * allocate memory for fault pixel table and copy the user
- * values to the table
- */
- if (!config_params->fault_pxl.enable)
- return 0;
-
- fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
- fpc_virtaddr = (unsigned int *)phys_to_virt(
- (unsigned long)fpc_physaddr);
- /*
- * Allocate memory for FPC table if current
- * FPC table buffer is not big enough to
- * accommodate FPC Number requested
- */
- if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
- if (fpc_physaddr != NULL) {
- free_pages((unsigned long)fpc_virtaddr,
- get_order
- (config_params->fault_pxl.fp_num *
- FP_NUM_BYTES));
- }
-
- /* Allocate memory for FPC table */
- fpc_virtaddr =
- (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA,
- get_order(raw_params->
- fault_pxl.fp_num *
- FP_NUM_BYTES));
-
- if (fpc_virtaddr == NULL) {
- dev_dbg(ccdc_cfg.dev,
- "\nUnable to allocate memory for FPC");
- return -EFAULT;
- }
- fpc_physaddr =
- (unsigned int *)virt_to_phys((void *)fpc_virtaddr);
- }
-
- /* Copy number of fault pixels and FPC table */
- config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num;
- if (copy_from_user(fpc_virtaddr,
- (void __user *)raw_params->fault_pxl.fpc_table_addr,
- config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
- dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
- return -EFAULT;
- }
- config_params->fault_pxl.fpc_table_addr = (unsigned long)fpc_physaddr;
- return 0;
-}
-
static int ccdc_close(struct device *dev)
{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
-
- fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
-
- if (fpc_physaddr != NULL) {
- fpc_virtaddr = (unsigned int *)
- phys_to_virt((unsigned long)fpc_physaddr);
- free_pages((unsigned long)fpc_virtaddr,
- get_order(config_params->fault_pxl.fp_num *
- FP_NUM_BYTES));
- }
return 0;
}
@@ -339,29 +247,6 @@ static void ccdc_sbl_reset(void)
vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
}
-/* Parameter operations */
-static int ccdc_set_params(void __user *params)
-{
- struct ccdc_config_params_raw ccdc_raw_params;
- int x;
-
- if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
- return -EINVAL;
-
- x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
- if (x) {
- dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copyingccdc params, %d\n",
- x);
- return -EFAULT;
- }
-
- if (!validate_ccdc_param(&ccdc_raw_params)) {
- if (!ccdc_update_raw_params(&ccdc_raw_params))
- return 0;
- }
- return -EINVAL;
-}
-
/*
* ccdc_config_ycbcr()
* This function will configure CCDC for YCbCr video capture
@@ -489,32 +374,6 @@ static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
regw(val, CCDC_BLKCMP);
}
-static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
-{
- u32 val;
-
- /* Initially disable FPC */
- val = CCDC_FPC_DISABLE;
- regw(val, CCDC_FPC);
-
- if (!fpc->enable)
- return;
-
- /* Configure Fault pixel if needed */
- regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%lx to FPC_ADDR...\n",
- (fpc->fpc_table_addr));
- /* Write the FPC params with FPC disable */
- val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
- regw(val, CCDC_FPC);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
- /* read the FPC register */
- val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
- regw(val, CCDC_FPC);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
-}
-
/*
* ccdc_config_raw()
* This function will configure CCDC for Raw capture mode
@@ -569,9 +428,6 @@ static void ccdc_config_raw(void)
/* Configure Black level compensation */
ccdc_config_black_compense(&config_params->blk_comp);
- /* Configure Fault Pixel Correction */
- ccdc_config_fpc(&config_params->fault_pxl);
-
/* If data size is 8 bit then pack the data */
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
@@ -929,7 +785,6 @@ static struct ccdc_hw_device ccdc_hw_dev = {
.reset = ccdc_sbl_reset,
.enable = ccdc_enable,
.set_hw_if_params = ccdc_set_hw_if_params,
- .set_params = ccdc_set_params,
.configure = ccdc_configure,
.set_buftype = ccdc_set_buftype,
.get_buftype = ccdc_get_buftype,
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index e3fe3e0635aa..b1bf4a7e8eb7 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -281,45 +281,6 @@ void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev)
EXPORT_SYMBOL(vpfe_unregister_ccdc_device);
/*
- * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
- */
-static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev,
- struct v4l2_format *f)
-{
- struct v4l2_rect image_win;
- enum ccdc_buftype buf_type;
- enum ccdc_frmfmt frm_fmt;
-
- memset(f, 0, sizeof(*f));
- f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ccdc_dev->hw_ops.get_image_window(&image_win);
- f->fmt.pix.width = image_win.width;
- f->fmt.pix.height = image_win.height;
- f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length();
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
- f->fmt.pix.height;
- buf_type = ccdc_dev->hw_ops.get_buftype();
- f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format();
- frm_fmt = ccdc_dev->hw_ops.get_frame_format();
- if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
- f->fmt.pix.field = V4L2_FIELD_NONE;
- else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
- if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED)
- f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
- else {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n");
- return -EINVAL;
- }
- } else {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/*
* vpfe_config_ccdc_image_format()
* For a pix format, configure ccdc to setup the capture
*/
@@ -1697,59 +1658,6 @@ unlock_out:
return ret;
}
-
-static long vpfe_param_handler(struct file *file, void *priv,
- bool valid_prio, unsigned int cmd, void *param)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- int ret;
-
- v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n");
-
- if (vpfe_dev->started) {
- /* only allowed if streaming is not started */
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "device already started\n");
- return -EBUSY;
- }
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- switch (cmd) {
- case VPFE_CMD_S_CCDC_RAW_PARAMS:
- v4l2_warn(&vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
- if (ccdc_dev->hw_ops.set_params) {
- ret = ccdc_dev->hw_ops.set_params(param);
- if (ret) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Error setting parameters in CCDC\n");
- goto unlock_out;
- }
- ret = vpfe_get_ccdc_image_format(vpfe_dev,
- &vpfe_dev->fmt);
- if (ret < 0) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Invalid image format at CCDC\n");
- goto unlock_out;
- }
- } else {
- ret = -EINVAL;
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
- }
- break;
- default:
- ret = -ENOTTY;
- }
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-
/* vpfe capture ioctl operations */
static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
.vidioc_querycap = vpfe_querycap,
@@ -1772,7 +1680,6 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
.vidioc_cropcap = vpfe_cropcap,
.vidioc_g_selection = vpfe_g_selection,
.vidioc_s_selection = vpfe_s_selection,
- .vidioc_default = vpfe_param_handler,
};
static struct vpfe_device *vpfe_initialize(void)
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 1b02a6363f77..07e89a4985a6 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -26,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/v4l2-dv-timings.h>
+#include <linux/of_graph.h>
#include "vpif.h"
@@ -423,7 +424,9 @@ EXPORT_SYMBOL(vpif_channel_getfid);
static int vpif_probe(struct platform_device *pdev)
{
- static struct resource *res;
+ static struct resource *res, *res_irq;
+ struct platform_device *pdev_capture, *pdev_display;
+ struct device_node *endpoint = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vpif_base = devm_ioremap_resource(&pdev->dev, res);
@@ -435,6 +438,58 @@ static int vpif_probe(struct platform_device *pdev)
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
+
+ /*
+ * If VPIF Node has endpoints, assume "new" DT support,
+ * where capture and display drivers don't have DT nodes
+ * so their devices need to be registered manually here
+ * for their legacy platform_drivers to work.
+ */
+ endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
+ endpoint);
+ if (!endpoint)
+ return 0;
+
+ /*
+ * For DT platforms, manually create platform_devices for
+ * capture/display drivers.
+ */
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq) {
+ dev_warn(&pdev->dev, "Missing IRQ resource.\n");
+ return -EINVAL;
+ }
+
+ pdev_capture = devm_kzalloc(&pdev->dev, sizeof(*pdev_capture),
+ GFP_KERNEL);
+ if (pdev_capture) {
+ pdev_capture->name = "vpif_capture";
+ pdev_capture->id = -1;
+ pdev_capture->resource = res_irq;
+ pdev_capture->num_resources = 1;
+ pdev_capture->dev.dma_mask = pdev->dev.dma_mask;
+ pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
+ pdev_capture->dev.parent = &pdev->dev;
+ platform_device_register(pdev_capture);
+ } else {
+ dev_warn(&pdev->dev, "Unable to allocate memory for pdev_capture.\n");
+ }
+
+ pdev_display = devm_kzalloc(&pdev->dev, sizeof(*pdev_display),
+ GFP_KERNEL);
+ if (pdev_display) {
+ pdev_display->name = "vpif_display";
+ pdev_display->id = -1;
+ pdev_display->resource = res_irq;
+ pdev_display->num_resources = 1;
+ pdev_display->dev.dma_mask = pdev->dev.dma_mask;
+ pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
+ pdev_display->dev.parent = &pdev->dev;
+ platform_device_register(pdev_display);
+ } else {
+ dev_warn(&pdev->dev, "Unable to allocate memory for pdev_display.\n");
+ }
+
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 44f702752d3a..4be6554c56c5 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -18,10 +18,16 @@
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-ioctl.h>
+#include <media/i2c/tvp514x.h>
+#include <media/v4l2-mediabus.h>
+
+#include <linux/videodev2.h>
#include "vpif.h"
#include "vpif_capture.h"
@@ -385,7 +391,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
common = &ch->common[i];
/* skip If streaming is not started in this channel */
/* Check the field format */
- if (1 == ch->vpifparams.std_info.frm_fmt) {
+ if (1 == ch->vpifparams.std_info.frm_fmt ||
+ common->fmt.fmt.pix.field == V4L2_FIELD_NONE) {
/* Progressive mode */
spin_lock(&common->irqlock);
if (list_empty(&common->dma_queue)) {
@@ -466,9 +473,38 @@ static int vpif_update_std_info(struct channel_obj *ch)
struct vpif_channel_config_params *std_info = &vpifparams->std_info;
struct video_obj *vid_ch = &ch->video;
int index;
+ struct v4l2_pix_format *pixfmt = &common->fmt.fmt.pix;
vpif_dbg(2, debug, "vpif_update_std_info\n");
+ /*
+ * if called after try_fmt or g_fmt, there will already be a size
+ * so use that by default.
+ */
+ if (pixfmt->width && pixfmt->height) {
+ if (pixfmt->field == V4L2_FIELD_ANY ||
+ pixfmt->field == V4L2_FIELD_NONE)
+ pixfmt->field = V4L2_FIELD_NONE;
+
+ vpifparams->iface.if_type = VPIF_IF_BT656;
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10 ||
+ pixfmt->pixelformat == V4L2_PIX_FMT_SBGGR8)
+ vpifparams->iface.if_type = VPIF_IF_RAW_BAYER;
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10)
+ vpifparams->params.data_sz = 1; /* 10 bits/pixel. */
+
+ /*
+ * For raw formats from camera sensors, we don't need
+ * the std_info from table lookup, so nothing else to do here.
+ */
+ if (vpifparams->iface.if_type == VPIF_IF_RAW_BAYER) {
+ memset(std_info, 0, sizeof(struct vpif_channel_config_params));
+ vpifparams->std_info.capture_format = 1; /* CCD/raw mode */
+ return 0;
+ }
+ }
+
for (index = 0; index < vpif_ch_params_count; index++) {
config = &vpif_ch_params[index];
if (config->hd_sd == 0) {
@@ -513,7 +549,7 @@ static int vpif_update_std_info(struct channel_obj *ch)
if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER)
common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
else
- common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV16;
common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -655,7 +691,7 @@ static int vpif_input_to_subdev(
/* loop through the sub device list to get the sub device info */
for (i = 0; i < vpif_cfg->subdev_count; i++) {
subdev_info = &vpif_cfg->subdev_info[i];
- if (!strcmp(subdev_info->name, subdev_name))
+ if (subdev_info && !strcmp(subdev_info->name, subdev_name))
return i;
}
return -1;
@@ -917,8 +953,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
fmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
} else {
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
- fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+ strcpy(fmt->description, "YCbCr4:2:2 Semi-Planar");
+ fmt->pixelformat = V4L2_PIX_FMT_NV16;
}
return 0;
}
@@ -936,22 +972,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
struct channel_obj *ch = video_get_drvdata(vdev);
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
- struct vpif_params *vpif_params = &ch->vpifparams;
-
- /*
- * to supress v4l-compliance warnings silently correct
- * the pixelformat
- */
- if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) {
- if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8)
- pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
- } else {
- if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
- pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P;
- }
-
- common->fmt.fmt.pix.pixelformat = pixfmt->pixelformat;
+ common->fmt = *fmt;
vpif_update_std_info(ch);
pixfmt->field = common->fmt.fmt.pix.field;
@@ -960,8 +982,17 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
pixfmt->width = common->fmt.fmt.pix.width;
pixfmt->height = common->fmt.fmt.pix.height;
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2;
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_SGRBG10) {
+ pixfmt->bytesperline = common->fmt.fmt.pix.width * 2;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+ }
pixfmt->priv = 0;
+ dev_dbg(vpif_dev, "%s: %d x %d; pitch=%d pixelformat=0x%08x, field=%d, size=%d\n", __func__,
+ pixfmt->width, pixfmt->height,
+ pixfmt->bytesperline, pixfmt->pixelformat,
+ pixfmt->field, pixfmt->sizeimage);
+
return 0;
}
@@ -978,13 +1009,47 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
struct video_device *vdev = video_devdata(file);
struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_pix_format *pix_fmt = &fmt->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &format.format;
+ int ret;
/* Check the validity of the buffer type */
if (common->fmt.type != fmt->type)
return -EINVAL;
- /* Fill in the information about format */
+ /* By default, use currently set fmt */
*fmt = common->fmt;
+
+ /* If subdev has get_fmt, use that to override */
+ ret = v4l2_subdev_call(ch->sd, pad, get_fmt, NULL, &format);
+ if (!ret && mbus_fmt->code) {
+ v4l2_fill_pix_format(pix_fmt, mbus_fmt);
+ pix_fmt->bytesperline = pix_fmt->width;
+ if (mbus_fmt->code == MEDIA_BUS_FMT_SGRBG10_1X10) {
+ /* e.g. mt9v032 */
+ pix_fmt->pixelformat = V4L2_PIX_FMT_SGRBG10;
+ pix_fmt->bytesperline = pix_fmt->width * 2;
+ } else if (mbus_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) {
+ /* e.g. tvp514x */
+ pix_fmt->pixelformat = V4L2_PIX_FMT_NV16;
+ pix_fmt->bytesperline = pix_fmt->width * 2;
+ } else {
+ dev_warn(vpif_dev, "%s: Unhandled media-bus format 0x%x\n",
+ __func__, mbus_fmt->code);
+ }
+ pix_fmt->sizeimage = pix_fmt->bytesperline * pix_fmt->height;
+ dev_dbg(vpif_dev, "%s: %d x %d; pitch=%d, pixelformat=0x%08x, code=0x%x, field=%d, size=%d\n", __func__,
+ pix_fmt->width, pix_fmt->height,
+ pix_fmt->bytesperline, pix_fmt->pixelformat,
+ mbus_fmt->code, pix_fmt->field, pix_fmt->sizeimage);
+
+ common->fmt = *fmt;
+ vpif_update_std_info(ch);
+ }
+
return 0;
}
@@ -1323,6 +1388,22 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,
{
int i;
+ for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) {
+ struct v4l2_async_subdev *_asd = vpif_obj.config->asd[i];
+ const struct fwnode_handle *fwnode = _asd->match.fwnode.fwnode;
+
+ if (fwnode == subdev->fwnode) {
+ vpif_obj.sd[i] = subdev;
+ vpif_obj.config->chan_config->inputs[i].subdev_name =
+ (char *)to_of_node(subdev->fwnode)->full_name;
+ vpif_dbg(2, debug,
+ "%s: setting input %d subdev_name = %s\n",
+ __func__, i,
+ to_of_node(subdev->fwnode)->full_name);
+ return 0;
+ }
+ }
+
for (i = 0; i < vpif_obj.config->subdev_count; i++)
if (!strcmp(vpif_obj.config->subdev_info[i].name,
subdev->name)) {
@@ -1356,6 +1437,7 @@ static int vpif_probe_complete(void)
/* set initial format */
ch->video.stdid = V4L2_STD_525_60;
memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
+ common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vpif_update_std_info(ch);
/* Initialize vb2 queue */
@@ -1418,6 +1500,106 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier)
return vpif_probe_complete();
}
+static struct vpif_capture_config *
+vpif_capture_get_pdata(struct platform_device *pdev)
+{
+ struct device_node *endpoint = NULL;
+ struct v4l2_fwnode_endpoint bus_cfg;
+ struct vpif_capture_config *pdata;
+ struct vpif_subdev_info *sdinfo;
+ struct vpif_capture_chan_config *chan;
+ unsigned int i;
+
+ /*
+ * DT boot: OF node from parent device contains
+ * video ports & endpoints data.
+ */
+ if (pdev->dev.parent && pdev->dev.parent->of_node)
+ pdev->dev.of_node = pdev->dev.parent->of_node;
+ if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+ return pdev->dev.platform_data;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+ pdata->subdev_info =
+ devm_kzalloc(&pdev->dev, sizeof(*pdata->subdev_info) *
+ VPIF_CAPTURE_NUM_CHANNELS, GFP_KERNEL);
+
+ if (!pdata->subdev_info)
+ return NULL;
+
+ for (i = 0; i < VPIF_CAPTURE_NUM_CHANNELS; i++) {
+ struct device_node *rem;
+ unsigned int flags;
+ int err;
+
+ endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
+ endpoint);
+ if (!endpoint)
+ break;
+
+ sdinfo = &pdata->subdev_info[i];
+ chan = &pdata->chan_config[i];
+ chan->inputs = devm_kzalloc(&pdev->dev,
+ sizeof(*chan->inputs) *
+ VPIF_CAPTURE_NUM_CHANNELS,
+ GFP_KERNEL);
+
+ chan->input_count++;
+ chan->inputs[i].input.type = V4L2_INPUT_TYPE_CAMERA;
+ chan->inputs[i].input.std = V4L2_STD_ALL;
+ chan->inputs[i].input.capabilities = V4L2_IN_CAP_STD;
+
+ err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+ &bus_cfg);
+ if (err) {
+ dev_err(&pdev->dev, "Could not parse the endpoint\n");
+ goto done;
+ }
+ dev_dbg(&pdev->dev, "Endpoint %s, bus_width = %d\n",
+ endpoint->full_name, bus_cfg.bus.parallel.bus_width);
+ flags = bus_cfg.bus.parallel.flags;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ chan->vpif_if.hd_pol = 1;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ chan->vpif_if.vd_pol = 1;
+
+ rem = of_graph_get_remote_port_parent(endpoint);
+ if (!rem) {
+ dev_dbg(&pdev->dev, "Remote device at %s not found\n",
+ endpoint->full_name);
+ goto done;
+ }
+
+ dev_dbg(&pdev->dev, "Remote device %s, %s found\n",
+ rem->name, rem->full_name);
+ sdinfo->name = rem->full_name;
+
+ pdata->asd[i] = devm_kzalloc(&pdev->dev,
+ sizeof(struct v4l2_async_subdev),
+ GFP_KERNEL);
+ if (!pdata->asd[i]) {
+ of_node_put(rem);
+ pdata = NULL;
+ goto done;
+ }
+
+ pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ pdata->asd[i]->match.fwnode.fwnode = of_fwnode_handle(rem);
+ of_node_put(rem);
+ }
+
+done:
+ pdata->asd_sizes[0] = i;
+ pdata->subdev_count = i;
+ pdata->card_name = "DA850/OMAP-L138 Video Capture";
+
+ return pdata;
+}
+
/**
* vpif_probe : This function probes the vpif capture driver
* @pdev: platform device pointer
@@ -1434,6 +1616,12 @@ static __init int vpif_probe(struct platform_device *pdev)
int res_idx = 0;
int i, err;
+ pdev->dev.platform_data = vpif_capture_get_pdata(pdev);
+ if (!pdev->dev.platform_data) {
+ dev_warn(&pdev->dev, "Missing platform data. Giving up.\n");
+ return -EINVAL;
+ }
+
if (!pdev->dev.platform_data) {
dev_warn(&pdev->dev, "Missing platform data. Giving up.\n");
return -EINVAL;
@@ -1474,7 +1662,7 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_unregister;
}
- if (!vpif_obj.config->asd_sizes) {
+ if (!vpif_obj.config->asd_sizes[0]) {
int i2c_id = vpif_obj.config->i2c_adapter_id;
i2c_adap = i2c_get_adapter(i2c_id);
@@ -1531,7 +1719,6 @@ vpif_unregister:
*/
static int vpif_remove(struct platform_device *device)
{
- struct common_obj *common;
struct channel_obj *ch;
int i;
@@ -1542,7 +1729,6 @@ static int vpif_remove(struct platform_device *device)
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
/* Unregister video device */
video_unregister_device(&ch->video_dev);
kfree(vpif_obj.dev[i]);
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 7e5cf9923c8d..bf982bf86542 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -1250,6 +1250,11 @@ static __init int vpif_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (!pdev->dev.platform_data) {
+ dev_warn(&pdev->dev, "Missing platform data. Giving up.\n");
+ return -EINVAL;
+ }
+
vpif_dev = &pdev->dev;
err = initialize_vpif();
@@ -1334,7 +1339,6 @@ vpif_unregister:
*/
static int vpif_remove(struct platform_device *device)
{
- struct common_obj *common;
struct channel_obj *ch;
int i;
@@ -1345,7 +1349,6 @@ static int vpif_remove(struct platform_device *device)
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
/* Unregister video device */
video_unregister_device(&ch->video_dev);
kfree(vpif_obj.dev[i]);
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 59a634201830..43801509dabb 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -454,6 +454,7 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
} else {
min_w = variant->pix_min->target_rot_dis_w;
min_h = variant->pix_min->target_rot_dis_h;
+ pix_mp->colorspace = ctx->out_colorspace;
}
pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
@@ -472,10 +473,8 @@ int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
pix_mp->num_planes = fmt->num_planes;
- if (pix_mp->width >= 1280) /* HD */
- pix_mp->colorspace = V4L2_COLORSPACE_REC709;
- else /* SD */
- pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ ctx->out_colorspace = pix_mp->colorspace;
for (i = 0; i < pix_mp->num_planes; ++i) {
struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i];
@@ -519,8 +518,8 @@ int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
pix_mp->height = frame->f_height;
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->pixelformat = frame->fmt->pixelformat;
- pix_mp->colorspace = V4L2_COLORSPACE_REC709;
pix_mp->num_planes = frame->fmt->num_planes;
+ pix_mp->colorspace = ctx->out_colorspace;
for (i = 0; i < pix_mp->num_planes; ++i) {
pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
@@ -569,9 +568,9 @@ int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
}
pr_debug("user put w: %d, h: %d", cr->c.width, cr->c.height);
- if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
f = &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
f = &ctx->s_frame;
else
return -EINVAL;
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index 696217e9af66..715d9c9d8d30 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -376,6 +376,7 @@ struct gsc_ctx {
struct v4l2_ctrl_handler ctrl_handler;
struct gsc_ctrls gsc_ctrls;
bool ctrls_rdy;
+ enum v4l2_colorspace out_colorspace;
};
void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm);
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 82505025d96c..33611a46ce35 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -460,8 +460,8 @@ static int gsc_m2m_g_selection(struct file *file, void *fh,
struct gsc_frame *frame;
struct gsc_ctx *ctx = fh_to_ctx(fh);
- if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
- (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
return -EINVAL;
frame = ctx_get_frame(ctx, s->type);
@@ -503,8 +503,8 @@ static int gsc_m2m_s_selection(struct file *file, void *fh,
cr.type = s->type;
cr.c = s->r;
- if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
- (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
return -EINVAL;
ret = gsc_try_crop(ctx, &cr);
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 57d42c6172c5..c480efb755f5 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -4,6 +4,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on OF && COMMON_CLK
+ select V4L2_FWNODE
help
Say Y here to enable camera host interface devices for
Samsung S5P and EXYNOS SoC series.
@@ -32,6 +33,7 @@ config VIDEO_S5P_MIPI_CSIS
tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
depends on REGULATOR
select GENERIC_PHY
+ select V4L2_FWNODE
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
receiver (MIPI-CSIS) devices.
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 8a7cd07dbe28..948fe01f6c96 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -1270,13 +1270,14 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
struct fimc_frame *f = &ctx->s_frame;
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
+ /* fall through */
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
s->r.left = 0;
@@ -1287,6 +1288,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
case V4L2_SEL_TGT_COMPOSE:
f = &ctx->d_frame;
+ /* fall through */
case V4L2_SEL_TGT_CROP:
s->r.left = f->offs_h;
s->r.top = f->offs_v;
@@ -1320,7 +1322,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh,
struct fimc_frame *f;
unsigned long flags;
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (s->target == V4L2_SEL_TGT_COMPOSE)
@@ -1610,6 +1612,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
+ /* fall through */
case V4L2_SEL_TGT_CROP_BOUNDS:
r->width = f->o_width;
r->height = f->o_height;
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 7f92144a1de3..340d906db370 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -854,7 +854,7 @@ static int fimc_is_probe(struct platform_device *pdev)
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
- ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ ret = devm_of_platform_populate(dev);
if (ret < 0)
goto err_pm;
@@ -864,7 +864,7 @@ static int fimc_is_probe(struct platform_device *pdev)
*/
ret = fimc_is_register_subdevs(is);
if (ret < 0)
- goto err_of_dep;
+ goto err_pm;
ret = fimc_is_debugfs_create(is);
if (ret < 0)
@@ -883,8 +883,6 @@ err_dfs:
fimc_is_debugfs_remove(is);
err_sd:
fimc_is_unregister_subdevs(is);
-err_of_dep:
- of_platform_depopulate(dev);
err_pm:
if (!pm_runtime_enabled(dev))
fimc_is_runtime_suspend(dev);
@@ -946,7 +944,6 @@ static int fimc_is_remove(struct platform_device *pdev)
if (!pm_runtime_status_suspended(dev))
fimc_is_runtime_suspend(dev);
free_irq(is->irq, is);
- of_platform_depopulate(dev);
fimc_is_unregister_subdevs(is);
vb2_dma_contig_clear_max_seg_size(dev);
fimc_is_put_clocks(is);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index b4c4a33784c4..7d3ec5cc6608 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -901,7 +901,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh,
struct fimc_lite *fimc = video_drvdata(file);
struct flite_frame *f = &fimc->out_frame;
- if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
switch (sel->target) {
@@ -929,7 +929,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh,
struct v4l2_rect rect = sel->r;
unsigned long flags;
- if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index e82450e90a67..7d1cf78846c4 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/media-device.h>
#include <media/drv-intf/exynos-fimc.h>
@@ -388,7 +388,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
{
struct fimc_source_info *pd = &fmd->sensor[index].pdata;
struct device_node *rem, *ep, *np;
- struct v4l2_of_endpoint endpoint;
+ struct v4l2_fwnode_endpoint endpoint;
int ret;
/* Assume here a port node can have only one endpoint node. */
@@ -396,7 +396,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
if (!ep)
return 0;
- ret = v4l2_of_parse_endpoint(ep, &endpoint);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint);
if (ret) {
of_node_put(ep);
return ret;
@@ -453,8 +453,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
return -EINVAL;
}
- fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
- fmd->sensor[index].asd.match.of.node = rem;
+ fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ fmd->sensor[index].asd.match.fwnode.fwnode = of_fwnode_handle(rem);
fmd->async_subdevs[index] = &fmd->sensor[index].asd;
fmd->num_sensors++;
@@ -1361,7 +1361,8 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
/* Find platform data for this sensor subdev */
for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
- if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+ if (fmd->sensor[i].asd.match.fwnode.fwnode ==
+ of_fwnode_handle(subdev->dev->of_node))
si = &fmd->sensor[i];
if (si == NULL)
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index f819b29efc38..98c89873c2dc 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -30,7 +30,7 @@
#include <linux/spinlock.h>
#include <linux/videodev2.h>
#include <media/drv-intf/exynos-fimc.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "mipi-csis.h"
@@ -718,7 +718,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
struct csis_state *state)
{
struct device_node *node = pdev->dev.of_node;
- struct v4l2_of_endpoint endpoint;
+ struct v4l2_fwnode_endpoint endpoint;
int ret;
if (of_property_read_u32(node, "clock-frequency",
@@ -735,7 +735,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
return -EINVAL;
}
/* Get port node and validate MIPI-CSI channel id. */
- ret = v4l2_of_parse_endpoint(node, &endpoint);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint);
if (ret)
goto err;
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index a8bda6679422..8cac2f202099 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -393,6 +393,7 @@ static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
dma_free_coherent(cam->dev, cam->dma_buf_size,
cam->dma_bufs[0], cam->dma_handles[0]);
cam->nbufs = 0;
+ /* fall-through */
case 0:
cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
return -ENOMEM;
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
index 9e4eb7dcc424..81347558b24a 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
@@ -103,7 +103,7 @@ static int mtk_mdp_probe(struct platform_device *pdev)
{
struct mtk_mdp_dev *mdp;
struct device *dev = &pdev->dev;
- struct device_node *node;
+ struct device_node *node, *parent;
int i, ret = 0;
mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL);
@@ -117,8 +117,16 @@ static int mtk_mdp_probe(struct platform_device *pdev)
mutex_init(&mdp->lock);
mutex_init(&mdp->vpulock);
+ /* Old dts had the components as child nodes */
+ if (of_get_next_child(dev->of_node, NULL)) {
+ parent = dev->of_node;
+ dev_warn(dev, "device tree is out of date\n");
+ } else {
+ parent = dev->of_node->parent;
+ }
+
/* Iterate over sibling MDP function blocks */
- for_each_child_of_node(dev->of_node, node) {
+ for_each_child_of_node(parent, node) {
const struct of_device_id *of_id;
enum mtk_mdp_comp_type comp_type;
int comp_id;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
index a60b538686ea..843510979ad8 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
@@ -278,7 +278,7 @@ static void mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx)
clean_free_buffer(ctx);
}
-static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx)
+static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx)
{
unsigned int dpbsize = 0;
int ret;
@@ -288,7 +288,7 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx)
&ctx->last_decoded_picinfo)) {
mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR",
ctx->id);
- return;
+ return -EINVAL;
}
if (ctx->last_decoded_picinfo.pic_w == 0 ||
@@ -296,12 +296,12 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx)
ctx->last_decoded_picinfo.buf_w == 0 ||
ctx->last_decoded_picinfo.buf_h == 0) {
mtk_v4l2_err("Cannot get correct pic info");
- return;
+ return -EINVAL;
}
if ((ctx->last_decoded_picinfo.pic_w == ctx->picinfo.pic_w) ||
(ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h))
- return;
+ return 0;
mtk_v4l2_debug(1,
"[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)",
@@ -316,6 +316,8 @@ static void mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx)
mtk_v4l2_err("Incorrect dpb size, ret=%d", ret);
ctx->dpb_size = dpbsize;
+
+ return ret;
}
static void mtk_vdec_worker(struct work_struct *work)
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h
index 237e144c194f..06c254f5c171 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h
@@ -32,6 +32,15 @@ extern int mtk_v4l2_dbg_level;
extern bool mtk_vcodec_dbg;
+#define mtk_v4l2_err(fmt, args...) \
+ pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \
+ ##args)
+
+#define mtk_vcodec_err(h, fmt, args...) \
+ pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \
+ ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args)
+
+
#if defined(DEBUG)
#define mtk_v4l2_debug(level, fmt, args...) \
@@ -41,11 +50,6 @@ extern bool mtk_vcodec_dbg;
level, __func__, __LINE__, ##args); \
} while (0)
-#define mtk_v4l2_err(fmt, args...) \
- pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \
- ##args)
-
-
#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+")
#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-")
@@ -57,22 +61,16 @@ extern bool mtk_vcodec_dbg;
__func__, ##args); \
} while (0)
-#define mtk_vcodec_err(h, fmt, args...) \
- pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n", \
- ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args)
-
#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+")
#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-")
#else
#define mtk_v4l2_debug(level, fmt, args...) {}
-#define mtk_v4l2_err(fmt, args...) {}
#define mtk_v4l2_debug_enter() {}
#define mtk_v4l2_debug_leave() {}
#define mtk_vcodec_debug(h, fmt, args...) {}
-#define mtk_vcodec_err(h, fmt, args...) {}
#define mtk_vcodec_debug_enter(h) {}
#define mtk_vcodec_debug_leave(h) {}
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index 92c4e1826356..45a553d4f5b2 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -16,7 +16,6 @@
#include <media/videobuf-dma-contig.h>
#include <media/v4l2-device.h>
-#include <linux/omap-dma.h>
#include <video/omapvrfb.h>
#include "omap_voutdef.h"
@@ -63,7 +62,7 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
/*
* Wakes up the application once the DMA transfer to VRFB space is completed.
*/
-static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
+static void omap_vout_vrfb_dma_tx_callback(void *data)
{
struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
@@ -94,6 +93,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
int ret = 0, i, j;
struct omap_vout_device *vout;
struct video_device *vfd;
+ dma_cap_mask_t mask;
int image_width, image_height;
int vrfb_num_bufs = VRFB_NUM_BUFS;
struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
@@ -131,18 +131,27 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
/*
* Request and Initialize DMA, for DMA based VRFB transfer
*/
- vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
- vout->vrfb_dma_tx.dma_ch = -1;
- vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
- ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
- omap_vout_vrfb_dma_tx_callback,
- (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
- if (ret < 0) {
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_INTERLEAVE, mask);
+ vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask);
+ if (IS_ERR(vout->vrfb_dma_tx.chan)) {
vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ } else {
+ size_t xt_size = sizeof(struct dma_interleaved_template) +
+ sizeof(struct data_chunk);
+
+ vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL);
+ if (!vout->vrfb_dma_tx.xt) {
+ dma_release_channel(vout->vrfb_dma_tx.chan);
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ }
+ }
+
+ if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED)
dev_info(&pdev->dev,
": failed to allocate DMA Channel for video%d\n",
vfd->minor);
- }
+
init_waitqueue_head(&vout->vrfb_dma_tx.wait);
/* statically allocated the VRFB buffer is done through
@@ -177,7 +186,9 @@ void omap_vout_release_vrfb(struct omap_vout_device *vout)
if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
- omap_free_dma(vout->vrfb_dma_tx.dma_ch);
+ kfree(vout->vrfb_dma_tx.xt);
+ dmaengine_terminate_sync(vout->vrfb_dma_tx.chan);
+ dma_release_channel(vout->vrfb_dma_tx.chan);
}
}
@@ -219,70 +230,84 @@ int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
}
int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
- struct videobuf_buffer *vb)
+ struct videobuf_buffer *vb)
{
- dma_addr_t dmabuf;
- struct vid_vrfb_dma *tx;
+ struct dma_async_tx_descriptor *tx;
+ enum dma_ctrl_flags flags;
+ struct dma_chan *chan = vout->vrfb_dma_tx.chan;
+ struct dma_device *dmadev = chan->device;
+ struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt;
+ dma_cookie_t cookie;
+ enum dma_status status;
enum dss_rotation rotation;
- u32 dest_frame_index = 0, src_element_index = 0;
- u32 dest_element_index = 0, src_frame_index = 0;
- u32 elem_count = 0, frame_count = 0, pixsize = 2;
+ size_t dst_icg;
+ u32 pixsize;
if (!is_rotation_enabled(vout))
return 0;
- dmabuf = vout->buf_phy_addr[vb->i];
/* If rotation is enabled, copy input buffer into VRFB
* memory space using DMA. We are copying input buffer
* into VRFB memory space of desired angle and DSS will
* read image VRFB memory for 0 degree angle
*/
+
pixsize = vout->bpp * vout->vrfb_bpp;
- /*
- * DMA transfer in double index mode
- */
+ dst_icg = ((MAX_PIXELS_PER_LINE * pixsize) -
+ (vout->pix.width * vout->bpp)) + 1;
+
+ xt->src_start = vout->buf_phy_addr[vb->i];
+ xt->dst_start = vout->vrfb_context[vb->i].paddr[0];
+
+ xt->numf = vout->pix.height;
+ xt->frame_size = 1;
+ xt->sgl[0].size = vout->pix.width * vout->bpp;
+ xt->sgl[0].icg = dst_icg;
+
+ xt->dir = DMA_MEM_TO_MEM;
+ xt->src_sgl = false;
+ xt->src_inc = true;
+ xt->dst_sgl = true;
+ xt->dst_inc = true;
+
+ tx = dmadev->device_prep_interleaved_dma(chan, xt, flags);
+ if (tx == NULL) {
+ pr_err("%s: DMA interleaved prep error\n", __func__);
+ return -EINVAL;
+ }
- /* Frame index */
- dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
- (vout->pix.width * vout->bpp)) + 1;
-
- /* Source and destination parameters */
- src_element_index = 0;
- src_frame_index = 0;
- dest_element_index = 1;
- /* Number of elements per frame */
- elem_count = vout->pix.width * vout->bpp;
- frame_count = vout->pix.height;
- tx = &vout->vrfb_dma_tx;
- tx->tx_status = 0;
- omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
- tx->dev_id, 0x0);
- /* src_port required only for OMAP1 */
- omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dmabuf, src_element_index, src_frame_index);
- /*set dma source burst mode for VRFB */
- omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
- rotation = calc_rotation(vout);
+ tx->callback = omap_vout_vrfb_dma_tx_callback;
+ tx->callback_param = &vout->vrfb_dma_tx;
+
+ cookie = dmaengine_submit(tx);
+ if (dma_submit_error(cookie)) {
+ pr_err("%s: dmaengine_submit failed (%d)\n", __func__, cookie);
+ return -EINVAL;
+ }
- /* dest_port required only for OMAP1 */
- omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
- vout->vrfb_context[vb->i].paddr[0], dest_element_index,
- dest_frame_index);
- /*set dma dest burst mode for VRFB */
- omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
- omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
+ vout->vrfb_dma_tx.tx_status = 0;
+ dma_async_issue_pending(chan);
- omap_start_dma(tx->dma_ch);
- wait_event_interruptible_timeout(tx->wait, tx->tx_status == 1,
+ wait_event_interruptible_timeout(vout->vrfb_dma_tx.wait,
+ vout->vrfb_dma_tx.tx_status == 1,
VRFB_TX_TIMEOUT);
- if (tx->tx_status == 0) {
- omap_stop_dma(tx->dma_ch);
+ status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
+
+ if (vout->vrfb_dma_tx.tx_status == 0) {
+ pr_err("%s: Timeout while waiting for DMA\n", __func__);
+ dmaengine_terminate_sync(chan);
+ return -EINVAL;
+ } else if (status != DMA_COMPLETE) {
+ pr_err("%s: DMA completion %s status\n", __func__,
+ status == DMA_ERROR ? "error" : "busy");
+ dmaengine_terminate_sync(chan);
return -EINVAL;
}
+
/* Store buffers physical address into an array. Addresses
* from this array will be used to configure DSS */
+ rotation = calc_rotation(vout);
vout->queued_buf_addr[vb->i] = (u8 *)
vout->vrfb_context[vb->i].paddr[rotation];
return 0;
diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/omap/omap_voutdef.h
index 80c79fabdf95..56b630b1c8b4 100644
--- a/drivers/media/platform/omap/omap_voutdef.h
+++ b/drivers/media/platform/omap/omap_voutdef.h
@@ -14,6 +14,7 @@
#include <media/v4l2-ctrls.h>
#include <video/omapfb_dss.h>
#include <video/omapvrfb.h>
+#include <linux/dmaengine.h>
#define YUYV_BPP 2
#define RGB565_BPP 2
@@ -81,8 +82,9 @@ enum vout_rotaion_type {
* for VRFB hidden buffer
*/
struct vid_vrfb_dma {
- int dev_id;
- int dma_ch;
+ struct dma_chan *chan;
+ struct dma_interleaved_template *xt;
+
int req_status;
int tx_status;
wait_queue_head_t wait;
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 0d984a28a003..9df64c189883 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -55,6 +55,7 @@
#include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/sched.h>
@@ -63,9 +64,9 @@
#include <asm/dma-iommu.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mc.h>
-#include <media/v4l2-of.h>
#include "isp.h"
#include "ispreg.h"
@@ -2007,20 +2008,20 @@ enum isp_of_phy {
ISP_OF_PHY_CSIPHY2,
};
-static int isp_of_parse_node(struct device *dev, struct device_node *node,
- struct isp_async_subdev *isd)
+static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,
+ struct isp_async_subdev *isd)
{
struct isp_bus_cfg *buscfg = &isd->bus;
- struct v4l2_of_endpoint vep;
+ struct v4l2_fwnode_endpoint vep;
unsigned int i;
int ret;
- ret = v4l2_of_parse_endpoint(node, &vep);
+ ret = v4l2_fwnode_endpoint_parse(fwnode, &vep);
if (ret)
return ret;
- dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name,
- vep.base.port);
+ dev_dbg(dev, "parsing endpoint %s, interface %u\n",
+ to_of_node(fwnode)->full_name, vep.base.port);
switch (vep.base.port) {
case ISP_OF_PHY_PARALLEL:
@@ -2077,18 +2078,18 @@ static int isp_of_parse_node(struct device *dev, struct device_node *node,
break;
default:
- dev_warn(dev, "%s: invalid interface %u\n", node->full_name,
- vep.base.port);
+ dev_warn(dev, "%s: invalid interface %u\n",
+ to_of_node(fwnode)->full_name, vep.base.port);
break;
}
return 0;
}
-static int isp_of_parse_nodes(struct device *dev,
- struct v4l2_async_notifier *notifier)
+static int isp_fwnodes_parse(struct device *dev,
+ struct v4l2_async_notifier *notifier)
{
- struct device_node *node = NULL;
+ struct fwnode_handle *fwnode = NULL;
notifier->subdevs = devm_kcalloc(
dev, ISP_MAX_SUBDEVS, sizeof(*notifier->subdevs), GFP_KERNEL);
@@ -2096,7 +2097,8 @@ static int isp_of_parse_nodes(struct device *dev,
return -ENOMEM;
while (notifier->num_subdevs < ISP_MAX_SUBDEVS &&
- (node = of_graph_get_next_endpoint(dev->of_node, node))) {
+ (fwnode = fwnode_graph_get_next_endpoint(
+ of_fwnode_handle(dev->of_node), fwnode))) {
struct isp_async_subdev *isd;
isd = devm_kzalloc(dev, sizeof(*isd), GFP_KERNEL);
@@ -2105,23 +2107,24 @@ static int isp_of_parse_nodes(struct device *dev,
notifier->subdevs[notifier->num_subdevs] = &isd->asd;
- if (isp_of_parse_node(dev, node, isd))
+ if (isp_fwnode_parse(dev, fwnode, isd))
goto error;
- isd->asd.match.of.node = of_graph_get_remote_port_parent(node);
- if (!isd->asd.match.of.node) {
+ isd->asd.match.fwnode.fwnode =
+ fwnode_graph_get_remote_port_parent(fwnode);
+ if (!isd->asd.match.fwnode.fwnode) {
dev_warn(dev, "bad remote port parent\n");
goto error;
}
- isd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+ isd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
notifier->num_subdevs++;
}
return notifier->num_subdevs;
error:
- of_node_put(node);
+ fwnode_handle_put(fwnode);
return -EINVAL;
}
@@ -2192,8 +2195,8 @@ static int isp_probe(struct platform_device *pdev)
return -ENOMEM;
}
- ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type",
- &isp->phy_type);
+ ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node),
+ "ti,phy-type", &isp->phy_type);
if (ret)
return ret;
@@ -2202,12 +2205,12 @@ static int isp_probe(struct platform_device *pdev)
if (IS_ERR(isp->syscon))
return PTR_ERR(isp->syscon);
- ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1,
- &isp->syscon_offset);
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "syscon", 1, &isp->syscon_offset);
if (ret)
return ret;
- ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier);
+ ret = isp_fwnodes_parse(&pdev->dev, &isp->notifier);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index 929006f65cc7..399095170b6e 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -25,6 +25,7 @@
#include <linux/mm.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -37,9 +38,11 @@
#include <media/v4l2-async.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/videobuf2-dma-sg.h>
@@ -345,6 +348,36 @@ static const struct pxa_mbus_lookup mbus_fmt[] = {
.layout = PXA_MBUS_LAYOUT_PACKED,
},
}, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .name = "Bayer 8 GBRG",
+ .bits_per_sample = 8,
+ .packing = PXA_MBUS_PACKING_NONE,
+ .order = PXA_MBUS_ORDER_LE,
+ .layout = PXA_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .name = "Bayer 8 GRBG",
+ .bits_per_sample = 8,
+ .packing = PXA_MBUS_PACKING_NONE,
+ .order = PXA_MBUS_ORDER_LE,
+ .layout = PXA_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .name = "Bayer 8 RGGB",
+ .bits_per_sample = 8,
+ .packing = PXA_MBUS_PACKING_NONE,
+ .order = PXA_MBUS_ORDER_LE,
+ .layout = PXA_MBUS_LAYOUT_PACKED,
+ },
+}, {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
@@ -445,16 +478,6 @@ static const struct pxa_mbus_lookup mbus_fmt[] = {
.layout = PXA_MBUS_LAYOUT_PACKED,
},
}, {
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .name = "Bayer 8 GRBG",
- .bits_per_sample = 8,
- .packing = PXA_MBUS_PACKING_NONE,
- .order = PXA_MBUS_ORDER_LE,
- .layout = PXA_MBUS_LAYOUT_PACKED,
- },
-}, {
.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10DPCM8,
@@ -555,6 +578,9 @@ static s32 pxa_mbus_bytes_per_line(u32 width, const struct pxa_mbus_pixelfmt *mf
static s32 pxa_mbus_image_size(const struct pxa_mbus_pixelfmt *mf,
u32 bytes_per_line, u32 height)
{
+ if (mf->layout == PXA_MBUS_LAYOUT_PACKED)
+ return bytes_per_line * height;
+
switch (mf->packing) {
case PXA_MBUS_PACKING_2X8_PADHI:
return bytes_per_line * height * 2;
@@ -1099,7 +1125,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev,
/* mclk <= ciclk / 4 (27.4.2) */
if (mclk > lcdclk / 4) {
mclk = lcdclk / 4;
- dev_warn(pcdev_to_dev(pcdev),
+ dev_warn(&pdev->dev,
"Limiting master clock to %lu\n", mclk);
}
@@ -1110,7 +1136,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev,
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
pcdev->mclk = lcdclk / (2 * (div + 1));
- dev_dbg(pcdev_to_dev(pcdev), "LCD clock %luHz, target freq %luHz, divisor %u\n",
+ dev_dbg(&pdev->dev, "LCD clock %luHz, target freq %luHz, divisor %u\n",
lcdclk, mclk, div);
return div;
@@ -1291,6 +1317,7 @@ static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev,
* transformation. Note that UYVY is the only format that
* should be used if pxa framebuffer Overlay2 is used.
*/
+ /* fall through */
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
@@ -2066,6 +2093,8 @@ static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = {
.vidioc_g_register = pxac_vidioc_g_register,
.vidioc_s_register = pxac_vidioc_s_register,
#endif
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct v4l2_clk_ops pxa_camera_mclk_ops = {
@@ -2177,6 +2206,12 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier,
pxa_dma_stop_channels(pcdev);
pxa_camera_destroy_formats(pcdev);
+
+ if (pcdev->mclk_clk) {
+ v4l2_clk_unregister(pcdev->mclk_clk);
+ pcdev->mclk_clk = NULL;
+ }
+
video_unregister_device(&pcdev->vdev);
pcdev->sensor = NULL;
@@ -2236,7 +2271,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
{
u32 mclk_rate;
struct device_node *remote, *np = dev->of_node;
- struct v4l2_of_endpoint ep;
+ struct v4l2_fwnode_endpoint ep;
int err = of_property_read_u32(np, "clock-frequency",
&mclk_rate);
if (!err) {
@@ -2250,7 +2285,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
return -EINVAL;
}
- err = v4l2_of_parse_endpoint(np, &ep);
+ err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep);
if (err) {
dev_err(dev, "could not parse endpoint\n");
goto out;
@@ -2287,10 +2322,10 @@ static int pxa_camera_pdata_from_dt(struct device *dev,
if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
pcdev->platform_flags |= PXA_CAMERA_PCLK_EN;
- asd->match_type = V4L2_ASYNC_MATCH_OF;
+ asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
remote = of_graph_get_remote_port(np);
if (remote) {
- asd->match.of.node = remote;
+ asd->match.fwnode.fwnode = of_fwnode_handle(remote);
of_node_put(remote);
} else {
dev_notice(dev, "no remote for %s\n", of_node_full_name(np));
@@ -2501,7 +2536,13 @@ static int pxa_camera_remove(struct platform_device *pdev)
dma_release_channel(pcdev->dma_chans[1]);
dma_release_channel(pcdev->dma_chans[2]);
- v4l2_clk_unregister(pcdev->mclk_clk);
+ v4l2_async_notifier_unregister(&pcdev->notifier);
+
+ if (pcdev->mclk_clk) {
+ v4l2_clk_unregister(pcdev->mclk_clk);
+ pcdev->mclk_clk = NULL;
+ }
+
v4l2_device_unregister(&pcdev->v4l2_dev);
dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile
new file mode 100644
index 000000000000..0fe9afb83697
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/Makefile
@@ -0,0 +1,11 @@
+# Makefile for Qualcomm Venus driver
+
+venus-core-objs += core.o helpers.o firmware.o \
+ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o
+
+venus-dec-objs += vdec.o vdec_ctrls.o
+venus-enc-objs += venc.o venc_ctrls.o
+
+obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-core.o
+obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-dec.o
+obj-$(CONFIG_VIDEO_QCOM_VENUS) += venus-enc.o
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
new file mode 100644
index 000000000000..41eef376eb2d
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pm_runtime.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+
+#include "core.h"
+#include "vdec.h"
+#include "venc.h"
+#include "firmware.h"
+
+static void venus_event_notify(struct venus_core *core, u32 event)
+{
+ struct venus_inst *inst;
+
+ switch (event) {
+ case EVT_SYS_WATCHDOG_TIMEOUT:
+ case EVT_SYS_ERROR:
+ break;
+ default:
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ core->sys_error = true;
+ list_for_each_entry(inst, &core->instances, list)
+ inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
+ mutex_unlock(&core->lock);
+
+ disable_irq_nosync(core->irq);
+
+ /*
+ * Delay recovery to ensure venus has completed any pending cache
+ * operations. Without this sleep, we see device reset when firmware is
+ * unloaded after a system error.
+ */
+ schedule_delayed_work(&core->work, msecs_to_jiffies(100));
+}
+
+static const struct hfi_core_ops venus_core_ops = {
+ .event_notify = venus_event_notify,
+};
+
+static void venus_sys_error_handler(struct work_struct *work)
+{
+ struct venus_core *core =
+ container_of(work, struct venus_core, work.work);
+ int ret = 0;
+
+ dev_warn(core->dev, "system error has occurred, starting recovery!\n");
+
+ pm_runtime_get_sync(core->dev);
+
+ hfi_core_deinit(core, true);
+ hfi_destroy(core);
+ mutex_lock(&core->lock);
+ venus_shutdown(core->dev);
+
+ pm_runtime_put_sync(core->dev);
+
+ ret |= hfi_create(core, &venus_core_ops);
+
+ pm_runtime_get_sync(core->dev);
+
+ ret |= venus_boot(core->dev, core->res->fwname);
+
+ ret |= hfi_core_resume(core, true);
+
+ enable_irq(core->irq);
+
+ mutex_unlock(&core->lock);
+
+ ret |= hfi_core_init(core);
+
+ pm_runtime_put_sync(core->dev);
+
+ if (ret) {
+ disable_irq_nosync(core->irq);
+ dev_warn(core->dev, "recovery failed (%d)\n", ret);
+ schedule_delayed_work(&core->work, msecs_to_jiffies(10));
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ core->sys_error = false;
+ mutex_unlock(&core->lock);
+}
+
+static int venus_clks_get(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ struct device *dev = core->dev;
+ unsigned int i;
+
+ for (i = 0; i < res->clks_num; i++) {
+ core->clks[i] = devm_clk_get(dev, res->clks[i]);
+ if (IS_ERR(core->clks[i]))
+ return PTR_ERR(core->clks[i]);
+ }
+
+ return 0;
+}
+
+static int venus_clks_enable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < res->clks_num; i++) {
+ ret = clk_prepare_enable(core->clks[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (i--)
+ clk_disable_unprepare(core->clks[i]);
+
+ return ret;
+}
+
+static void venus_clks_disable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ unsigned int i = res->clks_num;
+
+ while (i--)
+ clk_disable_unprepare(core->clks[i]);
+}
+
+static int venus_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct venus_core *core;
+ struct resource *r;
+ int ret;
+
+ core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+
+ core->dev = dev;
+ platform_set_drvdata(pdev, core);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ core->base = devm_ioremap_resource(dev, r);
+ if (IS_ERR(core->base))
+ return PTR_ERR(core->base);
+
+ core->irq = platform_get_irq(pdev, 0);
+ if (core->irq < 0)
+ return core->irq;
+
+ core->res = of_device_get_match_data(dev);
+ if (!core->res)
+ return -ENODEV;
+
+ ret = venus_clks_get(core);
+ if (ret)
+ return ret;
+
+ ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&core->instances);
+ mutex_init(&core->lock);
+ INIT_DELAYED_WORK(&core->work, venus_sys_error_handler);
+
+ ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, hfi_isr_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "venus", core);
+ if (ret)
+ return ret;
+
+ ret = hfi_create(core, &venus_core_ops);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(dev);
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err_runtime_disable;
+
+ ret = venus_boot(dev, core->res->fwname);
+ if (ret)
+ goto err_runtime_disable;
+
+ ret = hfi_core_resume(core, true);
+ if (ret)
+ goto err_venus_shutdown;
+
+ ret = hfi_core_init(core);
+ if (ret)
+ goto err_venus_shutdown;
+
+ ret = v4l2_device_register(dev, &core->v4l2_dev);
+ if (ret)
+ goto err_core_deinit;
+
+ ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (ret)
+ goto err_dev_unregister;
+
+ ret = pm_runtime_put_sync(dev);
+ if (ret)
+ goto err_dev_unregister;
+
+ return 0;
+
+err_dev_unregister:
+ v4l2_device_unregister(&core->v4l2_dev);
+err_core_deinit:
+ hfi_core_deinit(core, false);
+err_venus_shutdown:
+ venus_shutdown(dev);
+err_runtime_disable:
+ pm_runtime_set_suspended(dev);
+ pm_runtime_disable(dev);
+ hfi_destroy(core);
+ return ret;
+}
+
+static int venus_remove(struct platform_device *pdev)
+{
+ struct venus_core *core = platform_get_drvdata(pdev);
+ struct device *dev = core->dev;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ WARN_ON(ret < 0);
+
+ ret = hfi_core_deinit(core, true);
+ WARN_ON(ret);
+
+ hfi_destroy(core);
+ venus_shutdown(dev);
+ of_platform_depopulate(dev);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ return ret;
+}
+
+static __maybe_unused int venus_runtime_suspend(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ ret = hfi_core_suspend(core);
+
+ venus_clks_disable(core);
+
+ return ret;
+}
+
+static __maybe_unused int venus_runtime_resume(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ ret = venus_clks_enable(core);
+ if (ret)
+ return ret;
+
+ ret = hfi_core_resume(core, false);
+ if (ret)
+ goto err_clks_disable;
+
+ return 0;
+
+err_clks_disable:
+ venus_clks_disable(core);
+ return ret;
+}
+
+static const struct dev_pm_ops venus_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(venus_runtime_suspend, venus_runtime_resume, NULL)
+};
+
+static const struct freq_tbl msm8916_freq_table[] = {
+ { 352800, 228570000 }, /* 1920x1088 @ 30 + 1280x720 @ 30 */
+ { 244800, 160000000 }, /* 1920x1088 @ 30 */
+ { 108000, 100000000 }, /* 1280x720 @ 30 */
+};
+
+static const struct reg_val msm8916_reg_preset[] = {
+ { 0xe0020, 0x05555556 },
+ { 0xe0024, 0x05555556 },
+ { 0x80124, 0x00000003 },
+};
+
+static const struct venus_resources msm8916_res = {
+ .freq_tbl = msm8916_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(msm8916_freq_table),
+ .reg_tbl = msm8916_reg_preset,
+ .reg_tbl_size = ARRAY_SIZE(msm8916_reg_preset),
+ .clks = { "core", "iface", "bus", },
+ .clks_num = 3,
+ .max_load = 352800, /* 720p@30 + 1080p@30 */
+ .hfi_version = HFI_VERSION_1XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xddc00000 - 1,
+ .fwname = "qcom/venus-1.8/venus.mdt",
+};
+
+static const struct freq_tbl msm8996_freq_table[] = {
+ { 1944000, 490000000 }, /* 4k UHD @ 60 */
+ { 972000, 320000000 }, /* 4k UHD @ 30 */
+ { 489600, 150000000 }, /* 1080p @ 60 */
+ { 244800, 75000000 }, /* 1080p @ 30 */
+};
+
+static const struct reg_val msm8996_reg_preset[] = {
+ { 0x80010, 0xffffffff },
+ { 0x80018, 0x00001556 },
+ { 0x8001C, 0x00001556 },
+};
+
+static const struct venus_resources msm8996_res = {
+ .freq_tbl = msm8996_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(msm8996_freq_table),
+ .reg_tbl = msm8996_reg_preset,
+ .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset),
+ .clks = {"core", "iface", "bus", "mbus" },
+ .clks_num = 4,
+ .max_load = 2563200,
+ .hfi_version = HFI_VERSION_3XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xddc00000 - 1,
+ .fwname = "qcom/venus-4.2/venus.mdt",
+};
+
+static const struct of_device_id venus_dt_match[] = {
+ { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
+ { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, venus_dt_match);
+
+static struct platform_driver qcom_venus_driver = {
+ .probe = venus_probe,
+ .remove = venus_remove,
+ .driver = {
+ .name = "qcom-venus",
+ .of_match_table = venus_dt_match,
+ .pm = &venus_pm_ops,
+ },
+};
+module_platform_driver(qcom_venus_driver);
+
+MODULE_ALIAS("platform:qcom-venus");
+MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
new file mode 100644
index 000000000000..cba092bcb76d
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VENUS_CORE_H_
+#define __VENUS_CORE_H_
+
+#include <linux/list.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "hfi.h"
+
+#define VIDC_CLKS_NUM_MAX 4
+
+struct freq_tbl {
+ unsigned int load;
+ unsigned long freq;
+};
+
+struct reg_val {
+ u32 reg;
+ u32 value;
+};
+
+struct venus_resources {
+ u64 dma_mask;
+ const struct freq_tbl *freq_tbl;
+ unsigned int freq_tbl_size;
+ const struct reg_val *reg_tbl;
+ unsigned int reg_tbl_size;
+ const char * const clks[VIDC_CLKS_NUM_MAX];
+ unsigned int clks_num;
+ enum hfi_version hfi_version;
+ u32 max_load;
+ unsigned int vmem_id;
+ u32 vmem_size;
+ u32 vmem_addr;
+ const char *fwname;
+};
+
+struct venus_format {
+ u32 pixfmt;
+ unsigned int num_planes;
+ u32 type;
+};
+
+/**
+ * struct venus_core - holds core parameters valid for all instances
+ *
+ * @base: IO memory base address
+ * @irq: Venus irq
+ * @clks: an array of struct clk pointers
+ * @core0_clk: a struct clk pointer for core0
+ * @core1_clk: a struct clk pointer for core1
+ * @vdev_dec: a reference to video device structure for decoder instances
+ * @vdev_enc: a reference to video device structure for encoder instances
+ * @v4l2_dev: a holder for v4l2 device structure
+ * @res: a reference to venus resources structure
+ * @dev: convenience struct device pointer
+ * @dev_dec: convenience struct device pointer for decoder device
+ * @dev_enc: convenience struct device pointer for encoder device
+ * @lock: a lock for this strucure
+ * @instances: a list_head of all instances
+ * @insts_count: num of instances
+ * @state: the state of the venus core
+ * @done: a completion for sync HFI operations
+ * @error: an error returned during last HFI sync operations
+ * @sys_error: an error flag that signal system error event
+ * @core_ops: the core operations
+ * @enc_codecs: encoders supported by this core
+ * @dec_codecs: decoders supported by this core
+ * @max_sessions_supported: holds the maximum number of sessions
+ * @core_caps: core capabilities
+ * @priv: a private filed for HFI operations
+ * @ops: the core HFI operations
+ * @work: a delayed work for handling system fatal error
+ */
+struct venus_core {
+ void __iomem *base;
+ int irq;
+ struct clk *clks[VIDC_CLKS_NUM_MAX];
+ struct clk *core0_clk;
+ struct clk *core1_clk;
+ struct video_device *vdev_dec;
+ struct video_device *vdev_enc;
+ struct v4l2_device v4l2_dev;
+ const struct venus_resources *res;
+ struct device *dev;
+ struct device *dev_dec;
+ struct device *dev_enc;
+ struct mutex lock;
+ struct list_head instances;
+ atomic_t insts_count;
+ unsigned int state;
+ struct completion done;
+ unsigned int error;
+ bool sys_error;
+ const struct hfi_core_ops *core_ops;
+ u32 enc_codecs;
+ u32 dec_codecs;
+ unsigned int max_sessions_supported;
+#define ENC_ROTATION_CAPABILITY 0x1
+#define ENC_SCALING_CAPABILITY 0x2
+#define ENC_DEINTERLACE_CAPABILITY 0x4
+#define DEC_MULTI_STREAM_CAPABILITY 0x8
+ unsigned int core_caps;
+ void *priv;
+ const struct hfi_ops *ops;
+ struct delayed_work work;
+};
+
+struct vdec_controls {
+ u32 post_loop_deb_mode;
+ u32 profile;
+ u32 level;
+};
+
+struct venc_controls {
+ u16 gop_size;
+ u32 num_p_frames;
+ u32 num_b_frames;
+ u32 bitrate_mode;
+ u32 bitrate;
+ u32 bitrate_peak;
+
+ u32 h264_i_period;
+ u32 h264_entropy_mode;
+ u32 h264_i_qp;
+ u32 h264_p_qp;
+ u32 h264_b_qp;
+ u32 h264_min_qp;
+ u32 h264_max_qp;
+ u32 h264_loop_filter_mode;
+ u32 h264_loop_filter_alpha;
+ u32 h264_loop_filter_beta;
+
+ u32 vp8_min_qp;
+ u32 vp8_max_qp;
+
+ u32 multi_slice_mode;
+ u32 multi_slice_max_bytes;
+ u32 multi_slice_max_mb;
+
+ u32 header_mode;
+
+ struct {
+ u32 mpeg4;
+ u32 h264;
+ u32 vpx;
+ } profile;
+ struct {
+ u32 mpeg4;
+ u32 h264;
+ } level;
+};
+
+struct venus_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ dma_addr_t dma_addr;
+ u32 size;
+ struct list_head reg_list;
+ u32 flags;
+ struct list_head ref_list;
+};
+
+#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
+
+/**
+ * struct venus_inst - holds per instance paramerters
+ *
+ * @list: used for attach an instance to the core
+ * @lock: instance lock
+ * @core: a reference to the core struct
+ * @internalbufs: a list of internal bufferes
+ * @registeredbufs: a list of registered capture bufferes
+ * @delayed_process a list of delayed buffers
+ * @delayed_process_work: a work_struct for process delayed buffers
+ * @ctrl_handler: v4l control handler
+ * @controls: a union of decoder and encoder control parameters
+ * @fh: a holder of v4l file handle structure
+ * @streamon_cap: stream on flag for capture queue
+ * @streamon_out: stream on flag for output queue
+ * @cmd_stop: a flag to signal encoder/decoder commands
+ * @width: current capture width
+ * @height: current capture height
+ * @out_width: current output width
+ * @out_height: current output height
+ * @colorspace: current color space
+ * @quantization: current quantization
+ * @xfer_func: current xfer function
+ * @fps: holds current FPS
+ * @timeperframe: holds current time per frame structure
+ * @fmt_out: a reference to output format structure
+ * @fmt_cap: a reference to capture format structure
+ * @num_input_bufs: holds number of input buffers
+ * @num_output_bufs: holds number of output buffers
+ * @input_buf_size holds input buffer size
+ * @output_buf_size: holds output buffer size
+ * @reconfig: a flag raised by decoder when the stream resolution changed
+ * @reconfig_width: holds the new width
+ * @reconfig_height: holds the new height
+ * @sequence_cap: a sequence counter for capture queue
+ * @sequence_out: a sequence counter for output queue
+ * @m2m_dev: a reference to m2m device structure
+ * @m2m_ctx: a reference to m2m context structure
+ * @state: current state of the instance
+ * @done: a completion for sync HFI operation
+ * @error: an error returned during last HFI sync operation
+ * @session_error: a flag rised by HFI interface in case of session error
+ * @ops: HFI operations
+ * @priv: a private for HFI operations callbacks
+ * @session_type: the type of the session (decoder or encoder)
+ * @hprop: a union used as a holder by get property
+ * @cap_width: width capability
+ * @cap_height: height capability
+ * @cap_mbs_per_frame: macroblocks per frame capability
+ * @cap_mbs_per_sec: macroblocks per second capability
+ * @cap_framerate: framerate capability
+ * @cap_scale_x: horizontal scaling capability
+ * @cap_scale_y: vertical scaling capability
+ * @cap_bitrate: bitrate capability
+ * @cap_hier_p: hier capability
+ * @cap_ltr_count: LTR count capability
+ * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability
+ * @cap_bufs_mode_static: buffers allocation mode capability
+ * @cap_bufs_mode_dynamic: buffers allocation mode capability
+ * @pl_count: count of supported profiles/levels
+ * @pl: supported profiles/levels
+ * @bufreq: holds buffer requirements
+ */
+struct venus_inst {
+ struct list_head list;
+ struct mutex lock;
+ struct venus_core *core;
+ struct list_head internalbufs;
+ struct list_head registeredbufs;
+ struct list_head delayed_process;
+ struct work_struct delayed_process_work;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ union {
+ struct vdec_controls dec;
+ struct venc_controls enc;
+ } controls;
+ struct v4l2_fh fh;
+ unsigned int streamon_cap, streamon_out;
+ bool cmd_stop;
+ u32 width;
+ u32 height;
+ u32 out_width;
+ u32 out_height;
+ u32 colorspace;
+ u8 ycbcr_enc;
+ u8 quantization;
+ u8 xfer_func;
+ u64 fps;
+ struct v4l2_fract timeperframe;
+ const struct venus_format *fmt_out;
+ const struct venus_format *fmt_cap;
+ unsigned int num_input_bufs;
+ unsigned int num_output_bufs;
+ unsigned int input_buf_size;
+ unsigned int output_buf_size;
+ bool reconfig;
+ u32 reconfig_width;
+ u32 reconfig_height;
+ u32 sequence_cap;
+ u32 sequence_out;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ unsigned int state;
+ struct completion done;
+ unsigned int error;
+ bool session_error;
+ const struct hfi_inst_ops *ops;
+ u32 session_type;
+ union hfi_get_property hprop;
+ struct hfi_capability cap_width;
+ struct hfi_capability cap_height;
+ struct hfi_capability cap_mbs_per_frame;
+ struct hfi_capability cap_mbs_per_sec;
+ struct hfi_capability cap_framerate;
+ struct hfi_capability cap_scale_x;
+ struct hfi_capability cap_scale_y;
+ struct hfi_capability cap_bitrate;
+ struct hfi_capability cap_hier_p;
+ struct hfi_capability cap_ltr_count;
+ struct hfi_capability cap_secure_output2_threshold;
+ bool cap_bufs_mode_static;
+ bool cap_bufs_mode_dynamic;
+ unsigned int pl_count;
+ struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT];
+ struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX];
+};
+
+#define ctrl_to_inst(ctrl) \
+ container_of((ctrl)->handler, struct venus_inst, ctrl_handler)
+
+static inline struct venus_inst *to_inst(struct file *filp)
+{
+ return container_of(filp->private_data, struct venus_inst, fh);
+}
+
+static inline void *to_hfi_priv(struct venus_core *core)
+{
+ return core->priv;
+}
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
new file mode 100644
index 000000000000..521d4b36c090
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/qcom_scm.h>
+#include <linux/sizes.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "firmware.h"
+
+#define VENUS_PAS_ID 9
+#define VENUS_FW_MEM_SIZE (6 * SZ_1M)
+
+int venus_boot(struct device *dev, const char *fwname)
+{
+ const struct firmware *mdt;
+ struct device_node *node;
+ phys_addr_t mem_phys;
+ struct resource r;
+ ssize_t fw_size;
+ size_t mem_size;
+ void *mem_va;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) || !qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(dev, "no memory-region specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ mem_phys = r.start;
+ mem_size = resource_size(&r);
+
+ if (mem_size < VENUS_FW_MEM_SIZE)
+ return -EINVAL;
+
+ mem_va = memremap(r.start, mem_size, MEMREMAP_WC);
+ if (!mem_va) {
+ dev_err(dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, mem_size);
+ return -ENOMEM;
+ }
+
+ ret = request_firmware(&mdt, fwname, dev);
+ if (ret < 0)
+ goto err_unmap;
+
+ fw_size = qcom_mdt_get_size(mdt);
+ if (fw_size < 0) {
+ ret = fw_size;
+ release_firmware(mdt);
+ goto err_unmap;
+ }
+
+ ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys,
+ mem_size);
+
+ release_firmware(mdt);
+
+ if (ret)
+ goto err_unmap;
+
+ ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
+ if (ret)
+ goto err_unmap;
+
+err_unmap:
+ memunmap(mem_va);
+ return ret;
+}
+
+int venus_shutdown(struct device *dev)
+{
+ return qcom_scm_pas_shutdown(VENUS_PAS_ID);
+}
diff --git a/drivers/media/platform/qcom/venus/firmware.h b/drivers/media/platform/qcom/venus/firmware.h
new file mode 100644
index 000000000000..428efb56d339
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/firmware.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_FIRMWARE_H__
+#define __VENUS_FIRMWARE_H__
+
+struct device;
+
+int venus_boot(struct device *dev, const char *fwname);
+int venus_shutdown(struct device *dev);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
new file mode 100644
index 000000000000..5f4434c0a8f1
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/v4l2-mem2mem.h>
+#include <asm/div64.h>
+
+#include "core.h"
+#include "helpers.h"
+#include "hfi_helper.h"
+
+struct intbuf {
+ struct list_head list;
+ u32 type;
+ size_t size;
+ void *va;
+ dma_addr_t da;
+ unsigned long attrs;
+};
+
+static int intbufs_set_buffer(struct venus_inst *inst, u32 type)
+{
+ struct venus_core *core = inst->core;
+ struct device *dev = core->dev;
+ struct hfi_buffer_requirements bufreq;
+ struct hfi_buffer_desc bd;
+ struct intbuf *buf;
+ unsigned int i;
+ int ret;
+
+ ret = venus_helper_get_bufreq(inst, type, &bufreq);
+ if (ret)
+ return 0;
+
+ if (!bufreq.size)
+ return 0;
+
+ for (i = 0; i < bufreq.count_actual; i++) {
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ buf->type = bufreq.type;
+ buf->size = bufreq.size;
+ buf->attrs = DMA_ATTR_WRITE_COMBINE |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+ buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL,
+ buf->attrs);
+ if (!buf->va) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ memset(&bd, 0, sizeof(bd));
+ bd.buffer_size = buf->size;
+ bd.buffer_type = buf->type;
+ bd.num_buffers = 1;
+ bd.device_addr = buf->da;
+
+ ret = hfi_session_set_buffers(inst, &bd);
+ if (ret) {
+ dev_err(dev, "set session buffers failed\n");
+ goto dma_free;
+ }
+
+ list_add_tail(&buf->list, &inst->internalbufs);
+ }
+
+ return 0;
+
+dma_free:
+ dma_free_attrs(dev, buf->size, buf->va, buf->da, buf->attrs);
+fail:
+ kfree(buf);
+ return ret;
+}
+
+static int intbufs_unset_buffers(struct venus_inst *inst)
+{
+ struct hfi_buffer_desc bd = {0};
+ struct intbuf *buf, *n;
+ int ret = 0;
+
+ list_for_each_entry_safe(buf, n, &inst->internalbufs, list) {
+ bd.buffer_size = buf->size;
+ bd.buffer_type = buf->type;
+ bd.num_buffers = 1;
+ bd.device_addr = buf->da;
+ bd.response_required = true;
+
+ ret = hfi_session_unset_buffers(inst, &bd);
+
+ list_del_init(&buf->list);
+ dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da,
+ buf->attrs);
+ kfree(buf);
+ }
+
+ return ret;
+}
+
+static const unsigned int intbuf_types[] = {
+ HFI_BUFFER_INTERNAL_SCRATCH,
+ HFI_BUFFER_INTERNAL_SCRATCH_1,
+ HFI_BUFFER_INTERNAL_SCRATCH_2,
+ HFI_BUFFER_INTERNAL_PERSIST,
+ HFI_BUFFER_INTERNAL_PERSIST_1,
+};
+
+static int intbufs_alloc(struct venus_inst *inst)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) {
+ ret = intbufs_set_buffer(inst, intbuf_types[i]);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ intbufs_unset_buffers(inst);
+ return ret;
+}
+
+static int intbufs_free(struct venus_inst *inst)
+{
+ return intbufs_unset_buffers(inst);
+}
+
+static u32 load_per_instance(struct venus_inst *inst)
+{
+ u32 mbs;
+
+ if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP))
+ return 0;
+
+ mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16);
+
+ return mbs * inst->fps;
+}
+
+static u32 load_per_type(struct venus_core *core, u32 session_type)
+{
+ struct venus_inst *inst = NULL;
+ u32 mbs_per_sec = 0;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->session_type != session_type)
+ continue;
+
+ mbs_per_sec += load_per_instance(inst);
+ }
+ mutex_unlock(&core->lock);
+
+ return mbs_per_sec;
+}
+
+static int load_scale_clocks(struct venus_core *core)
+{
+ const struct freq_tbl *table = core->res->freq_tbl;
+ unsigned int num_rows = core->res->freq_tbl_size;
+ unsigned long freq = table[0].freq;
+ struct clk *clk = core->clks[0];
+ struct device *dev = core->dev;
+ u32 mbs_per_sec;
+ unsigned int i;
+ int ret;
+
+ mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) +
+ load_per_type(core, VIDC_SESSION_TYPE_DEC);
+
+ if (mbs_per_sec > core->res->max_load)
+ dev_warn(dev, "HW is overloaded, needed: %d max: %d\n",
+ mbs_per_sec, core->res->max_load);
+
+ if (!mbs_per_sec && num_rows > 1) {
+ freq = table[num_rows - 1].freq;
+ goto set_freq;
+ }
+
+ for (i = 0; i < num_rows; i++) {
+ if (mbs_per_sec > table[i].load)
+ break;
+ freq = table[i].freq;
+ }
+
+set_freq:
+
+ if (core->res->hfi_version == HFI_VERSION_3XX) {
+ ret = clk_set_rate(clk, freq);
+ ret |= clk_set_rate(core->core0_clk, freq);
+ ret |= clk_set_rate(core->core1_clk, freq);
+ } else {
+ ret = clk_set_rate(clk, freq);
+ }
+
+ if (ret) {
+ dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fill_buffer_desc(const struct venus_buffer *buf,
+ struct hfi_buffer_desc *bd, bool response)
+{
+ memset(bd, 0, sizeof(*bd));
+ bd->buffer_type = HFI_BUFFER_OUTPUT;
+ bd->buffer_size = buf->size;
+ bd->num_buffers = 1;
+ bd->device_addr = buf->dma_addr;
+ bd->response_required = response;
+}
+
+static void return_buf_error(struct venus_inst *inst,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ v4l2_m2m_src_buf_remove_by_buf(m2m_ctx, vbuf);
+ else
+ v4l2_m2m_src_buf_remove_by_buf(m2m_ctx, vbuf);
+
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+}
+
+static int
+session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ struct venus_buffer *buf = to_venus_buffer(vbuf);
+ struct vb2_buffer *vb = &vbuf->vb2_buf;
+ unsigned int type = vb->type;
+ struct hfi_frame_data fdata;
+ int ret;
+
+ memset(&fdata, 0, sizeof(fdata));
+ fdata.alloc_len = buf->size;
+ fdata.device_addr = buf->dma_addr;
+ fdata.timestamp = vb->timestamp;
+ do_div(fdata.timestamp, NSEC_PER_USEC);
+ fdata.flags = 0;
+ fdata.clnt_data = vbuf->vb2_buf.index;
+
+ if (!fdata.timestamp)
+ fdata.flags |= HFI_BUFFERFLAG_TIMESTAMPINVALID;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fdata.buffer_type = HFI_BUFFER_INPUT;
+ fdata.filled_len = vb2_get_plane_payload(vb, 0);
+ fdata.offset = vb->planes[0].data_offset;
+
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len)
+ fdata.flags |= HFI_BUFFERFLAG_EOS;
+ } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fdata.buffer_type = HFI_BUFFER_OUTPUT;
+ fdata.filled_len = 0;
+ fdata.offset = 0;
+ }
+
+ ret = hfi_session_process_buf(inst, &fdata);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static inline int is_reg_unreg_needed(struct venus_inst *inst)
+{
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC &&
+ inst->core->res->hfi_version == HFI_VERSION_3XX)
+ return 0;
+
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC &&
+ inst->cap_bufs_mode_dynamic &&
+ inst->core->res->hfi_version == HFI_VERSION_1XX)
+ return 0;
+
+ return 1;
+}
+
+static int session_unregister_bufs(struct venus_inst *inst)
+{
+ struct venus_buffer *buf, *n;
+ struct hfi_buffer_desc bd;
+ int ret = 0;
+
+ if (!is_reg_unreg_needed(inst))
+ return 0;
+
+ list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) {
+ fill_buffer_desc(buf, &bd, true);
+ ret = hfi_session_unset_buffers(inst, &bd);
+ list_del_init(&buf->reg_list);
+ }
+
+ return ret;
+}
+
+static int session_register_bufs(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ struct device *dev = core->dev;
+ struct hfi_buffer_desc bd;
+ struct venus_buffer *buf;
+ int ret = 0;
+
+ if (!is_reg_unreg_needed(inst))
+ return 0;
+
+ list_for_each_entry(buf, &inst->registeredbufs, reg_list) {
+ fill_buffer_desc(buf, &bd, false);
+ ret = hfi_session_set_buffers(inst, &bd);
+ if (ret) {
+ dev_err(dev, "%s: set buffer failed\n", __func__);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
+ struct hfi_buffer_requirements *req)
+{
+ u32 ptype = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS;
+ union hfi_get_property hprop;
+ unsigned int i;
+ int ret;
+
+ if (req)
+ memset(req, 0, sizeof(*req));
+
+ ret = hfi_session_get_property(inst, ptype, &hprop);
+ if (ret)
+ return ret;
+
+ ret = -EINVAL;
+
+ for (i = 0; i < HFI_BUFFER_TYPE_MAX; i++) {
+ if (hprop.bufreq[i].type != type)
+ continue;
+
+ if (req)
+ memcpy(req, &hprop.bufreq[i], sizeof(*req));
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(venus_helper_get_bufreq);
+
+int venus_helper_set_input_resolution(struct venus_inst *inst,
+ unsigned int width, unsigned int height)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE;
+ struct hfi_framesize fs;
+
+ fs.buffer_type = HFI_BUFFER_INPUT;
+ fs.width = width;
+ fs.height = height;
+
+ return hfi_session_set_property(inst, ptype, &fs);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution);
+
+int venus_helper_set_output_resolution(struct venus_inst *inst,
+ unsigned int width, unsigned int height)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE;
+ struct hfi_framesize fs;
+
+ fs.buffer_type = HFI_BUFFER_OUTPUT;
+ fs.width = width;
+ fs.height = height;
+
+ return hfi_session_set_property(inst, ptype, &fs);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution);
+
+int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
+ unsigned int output_bufs)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+ struct hfi_buffer_count_actual buf_count;
+ int ret;
+
+ buf_count.type = HFI_BUFFER_INPUT;
+ buf_count.count_actual = input_bufs;
+
+ ret = hfi_session_set_property(inst, ptype, &buf_count);
+ if (ret)
+ return ret;
+
+ buf_count.type = HFI_BUFFER_OUTPUT;
+ buf_count.count_actual = output_bufs;
+
+ return hfi_session_set_property(inst, ptype, &buf_count);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs);
+
+int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt)
+{
+ struct hfi_uncompressed_format_select fmt;
+ u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+ int ret;
+
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC)
+ fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ else if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ fmt.buffer_type = HFI_BUFFER_INPUT;
+ else
+ return -EINVAL;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ fmt.format = HFI_COLOR_FORMAT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ fmt.format = HFI_COLOR_FORMAT_NV21;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = hfi_session_set_property(inst, ptype, &fmt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_color_format);
+
+static void delayed_process_buf_func(struct work_struct *work)
+{
+ struct venus_buffer *buf, *n;
+ struct venus_inst *inst;
+ int ret;
+
+ inst = container_of(work, struct venus_inst, delayed_process_work);
+
+ mutex_lock(&inst->lock);
+
+ if (!(inst->streamon_out & inst->streamon_cap))
+ goto unlock;
+
+ list_for_each_entry_safe(buf, n, &inst->delayed_process, ref_list) {
+ if (buf->flags & HFI_BUFFERFLAG_READONLY)
+ continue;
+
+ ret = session_process_buf(inst, &buf->vb);
+ if (ret)
+ return_buf_error(inst, &buf->vb);
+
+ list_del_init(&buf->ref_list);
+ }
+unlock:
+ mutex_unlock(&inst->lock);
+}
+
+void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx)
+{
+ struct venus_buffer *buf;
+
+ list_for_each_entry(buf, &inst->registeredbufs, reg_list) {
+ if (buf->vb.vb2_buf.index == idx) {
+ buf->flags &= ~HFI_BUFFERFLAG_READONLY;
+ schedule_work(&inst->delayed_process_work);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(venus_helper_release_buf_ref);
+
+void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf)
+{
+ struct venus_buffer *buf = to_venus_buffer(vbuf);
+
+ buf->flags |= HFI_BUFFERFLAG_READONLY;
+}
+EXPORT_SYMBOL_GPL(venus_helper_acquire_buf_ref);
+
+static int is_buf_refed(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ struct venus_buffer *buf = to_venus_buffer(vbuf);
+
+ if (buf->flags & HFI_BUFFERFLAG_READONLY) {
+ list_add_tail(&buf->ref_list, &inst->delayed_process);
+ schedule_work(&inst->delayed_process_work);
+ return 1;
+ }
+
+ return 0;
+}
+
+struct vb2_v4l2_buffer *
+venus_helper_find_buf(struct venus_inst *inst, unsigned int type, u32 idx)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
+ else
+ return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
+}
+EXPORT_SYMBOL_GPL(venus_helper_find_buf);
+
+int venus_helper_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct venus_buffer *buf = to_venus_buffer(vbuf);
+ struct sg_table *sgt;
+
+ sgt = vb2_dma_sg_plane_desc(vb, 0);
+ if (!sgt)
+ return -EFAULT;
+
+ buf->size = vb2_plane_size(vb, 0);
+ buf->dma_addr = sg_dma_address(sgt->sgl);
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ list_add_tail(&buf->reg_list, &inst->registeredbufs);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init);
+
+int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ vb2_plane_size(vb, 0) < inst->output_buf_size)
+ return -EINVAL;
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ vb2_plane_size(vb, 0) < inst->input_buf_size)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare);
+
+void venus_helper_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ if (inst->cmd_stop) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ inst->cmd_stop = false;
+ goto unlock;
+ }
+
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+
+ if (!(inst->streamon_out & inst->streamon_cap))
+ goto unlock;
+
+ ret = is_buf_refed(inst, vbuf);
+ if (ret)
+ goto unlock;
+
+ ret = session_process_buf(inst, vbuf);
+ if (ret)
+ return_buf_error(inst, vbuf);
+
+unlock:
+ mutex_unlock(&inst->lock);
+}
+EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue);
+
+void venus_helper_buffers_done(struct venus_inst *inst,
+ enum vb2_buffer_state state)
+{
+ struct vb2_v4l2_buffer *buf;
+
+ while ((buf = v4l2_m2m_src_buf_remove(inst->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+ while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+}
+EXPORT_SYMBOL_GPL(venus_helper_buffers_done);
+
+void venus_helper_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(q);
+ struct venus_core *core = inst->core;
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ if (inst->streamon_out & inst->streamon_cap) {
+ ret = hfi_session_stop(inst);
+ ret |= hfi_session_unload_res(inst);
+ ret |= session_unregister_bufs(inst);
+ ret |= intbufs_free(inst);
+ ret |= hfi_session_deinit(inst);
+
+ if (inst->session_error || core->sys_error)
+ ret = -EIO;
+
+ if (ret)
+ hfi_session_abort(inst);
+
+ load_scale_clocks(core);
+ }
+
+ venus_helper_buffers_done(inst, VB2_BUF_STATE_ERROR);
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->streamon_out = 0;
+ else
+ inst->streamon_cap = 0;
+
+ mutex_unlock(&inst->lock);
+}
+EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming);
+
+int venus_helper_vb2_start_streaming(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ int ret;
+
+ ret = intbufs_alloc(inst);
+ if (ret)
+ return ret;
+
+ ret = session_register_bufs(inst);
+ if (ret)
+ goto err_bufs_free;
+
+ load_scale_clocks(core);
+
+ ret = hfi_session_load_res(inst);
+ if (ret)
+ goto err_unreg_bufs;
+
+ ret = hfi_session_start(inst);
+ if (ret)
+ goto err_unload_res;
+
+ return 0;
+
+err_unload_res:
+ hfi_session_unload_res(inst);
+err_unreg_bufs:
+ session_unregister_bufs(inst);
+err_bufs_free:
+ intbufs_free(inst);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(venus_helper_vb2_start_streaming);
+
+void venus_helper_m2m_device_run(void *priv)
+{
+ struct venus_inst *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buf, *n;
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) {
+ ret = session_process_buf(inst, &buf->vb);
+ if (ret)
+ return_buf_error(inst, &buf->vb);
+ }
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
+ ret = session_process_buf(inst, &buf->vb);
+ if (ret)
+ return_buf_error(inst, &buf->vb);
+ }
+
+ mutex_unlock(&inst->lock);
+}
+EXPORT_SYMBOL_GPL(venus_helper_m2m_device_run);
+
+void venus_helper_m2m_job_abort(void *priv)
+{
+ struct venus_inst *inst = priv;
+
+ v4l2_m2m_job_finish(inst->m2m_dev, inst->m2m_ctx);
+}
+EXPORT_SYMBOL_GPL(venus_helper_m2m_job_abort);
+
+void venus_helper_init_instance(struct venus_inst *inst)
+{
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC) {
+ INIT_LIST_HEAD(&inst->delayed_process);
+ INIT_WORK(&inst->delayed_process_work,
+ delayed_process_buf_func);
+ }
+}
+EXPORT_SYMBOL_GPL(venus_helper_init_instance);
diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
new file mode 100644
index 000000000000..6a061b417a93
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/helpers.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HELPERS_H__
+#define __VENUS_HELPERS_H__
+
+#include <media/videobuf2-v4l2.h>
+
+struct venus_inst;
+
+struct vb2_v4l2_buffer *venus_helper_find_buf(struct venus_inst *inst,
+ unsigned int type, u32 idx);
+void venus_helper_buffers_done(struct venus_inst *inst,
+ enum vb2_buffer_state state);
+int venus_helper_vb2_buf_init(struct vb2_buffer *vb);
+int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb);
+void venus_helper_vb2_buf_queue(struct vb2_buffer *vb);
+void venus_helper_vb2_stop_streaming(struct vb2_queue *q);
+int venus_helper_vb2_start_streaming(struct venus_inst *inst);
+void venus_helper_m2m_device_run(void *priv);
+void venus_helper_m2m_job_abort(void *priv);
+int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
+ struct hfi_buffer_requirements *req);
+int venus_helper_set_input_resolution(struct venus_inst *inst,
+ unsigned int width, unsigned int height);
+int venus_helper_set_output_resolution(struct venus_inst *inst,
+ unsigned int width, unsigned int height);
+int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
+ unsigned int output_bufs);
+int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt);
+void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf);
+void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx);
+void venus_helper_init_instance(struct venus_inst *inst);
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
new file mode 100644
index 000000000000..c09490876516
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include "core.h"
+#include "hfi.h"
+#include "hfi_cmds.h"
+#include "hfi_venus.h"
+
+#define TIMEOUT msecs_to_jiffies(1000)
+
+static u32 to_codec_type(u32 pixfmt)
+{
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_H264_NO_SC:
+ return HFI_VIDEO_CODEC_H264;
+ case V4L2_PIX_FMT_H263:
+ return HFI_VIDEO_CODEC_H263;
+ case V4L2_PIX_FMT_MPEG1:
+ return HFI_VIDEO_CODEC_MPEG1;
+ case V4L2_PIX_FMT_MPEG2:
+ return HFI_VIDEO_CODEC_MPEG2;
+ case V4L2_PIX_FMT_MPEG4:
+ return HFI_VIDEO_CODEC_MPEG4;
+ case V4L2_PIX_FMT_VC1_ANNEX_G:
+ case V4L2_PIX_FMT_VC1_ANNEX_L:
+ return HFI_VIDEO_CODEC_VC1;
+ case V4L2_PIX_FMT_VP8:
+ return HFI_VIDEO_CODEC_VP8;
+ case V4L2_PIX_FMT_VP9:
+ return HFI_VIDEO_CODEC_VP9;
+ case V4L2_PIX_FMT_XVID:
+ return HFI_VIDEO_CODEC_DIVX;
+ default:
+ return 0;
+ }
+}
+
+int hfi_core_init(struct venus_core *core)
+{
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+
+ if (core->state >= CORE_INIT)
+ goto unlock;
+
+ reinit_completion(&core->done);
+
+ ret = core->ops->core_init(core);
+ if (ret)
+ goto unlock;
+
+ ret = wait_for_completion_timeout(&core->done, TIMEOUT);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+
+ ret = 0;
+
+ if (core->error != HFI_ERR_NONE) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ core->state = CORE_INIT;
+unlock:
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+static int core_deinit_wait_atomic_t(atomic_t *p)
+{
+ schedule();
+ return 0;
+}
+
+int hfi_core_deinit(struct venus_core *core, bool blocking)
+{
+ int ret = 0, empty;
+
+ mutex_lock(&core->lock);
+
+ if (core->state == CORE_UNINIT)
+ goto unlock;
+
+ empty = list_empty(&core->instances);
+
+ if (!empty && !blocking) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (!empty) {
+ mutex_unlock(&core->lock);
+ wait_on_atomic_t(&core->insts_count, core_deinit_wait_atomic_t,
+ TASK_UNINTERRUPTIBLE);
+ mutex_lock(&core->lock);
+ }
+
+ ret = core->ops->core_deinit(core);
+
+ if (!ret)
+ core->state = CORE_UNINIT;
+
+unlock:
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+int hfi_core_suspend(struct venus_core *core)
+{
+ if (core->state != CORE_INIT)
+ return 0;
+
+ return core->ops->suspend(core);
+}
+
+int hfi_core_resume(struct venus_core *core, bool force)
+{
+ if (!force && core->state != CORE_INIT)
+ return 0;
+
+ return core->ops->resume(core);
+}
+
+int hfi_core_trigger_ssr(struct venus_core *core, u32 type)
+{
+ return core->ops->core_trigger_ssr(core, type);
+}
+
+int hfi_core_ping(struct venus_core *core)
+{
+ int ret;
+
+ mutex_lock(&core->lock);
+
+ ret = core->ops->core_ping(core, 0xbeef);
+ if (ret)
+ goto unlock;
+
+ ret = wait_for_completion_timeout(&core->done, TIMEOUT);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+ ret = 0;
+ if (core->error != HFI_ERR_NONE)
+ ret = -ENODEV;
+unlock:
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+static int wait_session_msg(struct venus_inst *inst)
+{
+ int ret;
+
+ ret = wait_for_completion_timeout(&inst->done, TIMEOUT);
+ if (!ret)
+ return -ETIMEDOUT;
+
+ if (inst->error != HFI_ERR_NONE)
+ return -EIO;
+
+ return 0;
+}
+
+int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
+{
+ struct venus_core *core = inst->core;
+
+ if (!ops)
+ return -EINVAL;
+
+ inst->state = INST_UNINIT;
+ init_completion(&inst->done);
+ inst->ops = ops;
+
+ mutex_lock(&core->lock);
+ list_add_tail(&inst->list, &core->instances);
+ atomic_inc(&core->insts_count);
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hfi_session_create);
+
+int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
+{
+ struct venus_core *core = inst->core;
+ const struct hfi_ops *ops = core->ops;
+ u32 codec;
+ int ret;
+
+ codec = to_codec_type(pixfmt);
+ reinit_completion(&inst->done);
+
+ ret = ops->session_init(inst, inst->session_type, codec);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_INIT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hfi_session_init);
+
+void hfi_session_destroy(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+
+ mutex_lock(&core->lock);
+ list_del_init(&inst->list);
+ atomic_dec(&core->insts_count);
+ wake_up_atomic_t(&core->insts_count);
+ mutex_unlock(&core->lock);
+}
+EXPORT_SYMBOL_GPL(hfi_session_destroy);
+
+int hfi_session_deinit(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state == INST_UNINIT)
+ return 0;
+
+ if (inst->state < INST_INIT)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_end(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_UNINIT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hfi_session_deinit);
+
+int hfi_session_start(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state != INST_LOAD_RESOURCES)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_start(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_START;
+
+ return 0;
+}
+
+int hfi_session_stop(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state != INST_START)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_stop(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_STOP;
+
+ return 0;
+}
+
+int hfi_session_continue(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+
+ if (core->res->hfi_version != HFI_VERSION_3XX)
+ return 0;
+
+ return core->ops->session_continue(inst);
+}
+EXPORT_SYMBOL_GPL(hfi_session_continue);
+
+int hfi_session_abort(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_abort(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int hfi_session_load_res(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state != INST_INIT)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_load_res(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_LOAD_RESOURCES;
+
+ return 0;
+}
+
+int hfi_session_unload_res(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state != INST_STOP)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_release_res(inst);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ inst->state = INST_RELEASE_RESOURCES;
+
+ return 0;
+}
+
+int hfi_session_flush(struct venus_inst *inst)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_flush(inst, HFI_FLUSH_ALL);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hfi_session_flush);
+
+int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+
+ return ops->session_set_buffers(inst, bd);
+}
+
+int hfi_session_unset_buffers(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_unset_buffers(inst, bd);
+ if (ret)
+ return ret;
+
+ if (!bd->response_required)
+ return 0;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
+ union hfi_get_property *hprop)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+ int ret;
+
+ if (inst->state < INST_INIT || inst->state >= INST_STOP)
+ return -EINVAL;
+
+ reinit_completion(&inst->done);
+
+ ret = ops->session_get_property(inst, ptype);
+ if (ret)
+ return ret;
+
+ ret = wait_session_msg(inst);
+ if (ret)
+ return ret;
+
+ *hprop = inst->hprop;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hfi_session_get_property);
+
+int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+
+ if (inst->state < INST_INIT || inst->state >= INST_STOP)
+ return -EINVAL;
+
+ return ops->session_set_property(inst, ptype, pdata);
+}
+EXPORT_SYMBOL_GPL(hfi_session_set_property);
+
+int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
+{
+ const struct hfi_ops *ops = inst->core->ops;
+
+ if (fd->buffer_type == HFI_BUFFER_INPUT)
+ return ops->session_etb(inst, fd);
+ else if (fd->buffer_type == HFI_BUFFER_OUTPUT)
+ return ops->session_ftb(inst, fd);
+
+ return -EINVAL;
+}
+
+irqreturn_t hfi_isr_thread(int irq, void *dev_id)
+{
+ struct venus_core *core = dev_id;
+
+ return core->ops->isr_thread(core);
+}
+
+irqreturn_t hfi_isr(int irq, void *dev)
+{
+ struct venus_core *core = dev;
+
+ return core->ops->isr(core);
+}
+
+int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
+{
+ int ret;
+
+ if (!ops)
+ return -EINVAL;
+
+ atomic_set(&core->insts_count, 0);
+ core->core_ops = ops;
+ core->state = CORE_UNINIT;
+ init_completion(&core->done);
+ pkt_set_version(core->res->hfi_version);
+ ret = venus_hfi_create(core);
+
+ return ret;
+}
+
+void hfi_destroy(struct venus_core *core)
+{
+ venus_hfi_destroy(core);
+}
diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h
new file mode 100644
index 000000000000..5466b7d60dd0
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __HFI_H__
+#define __HFI_H__
+
+#include <linux/interrupt.h>
+
+#include "hfi_helper.h"
+
+#define VIDC_SESSION_TYPE_VPE 0
+#define VIDC_SESSION_TYPE_ENC 1
+#define VIDC_SESSION_TYPE_DEC 2
+
+#define VIDC_RESOURCE_NONE 0
+#define VIDC_RESOURCE_OCMEM 1
+#define VIDC_RESOURCE_VMEM 2
+
+struct hfi_buffer_desc {
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 num_buffers;
+ u32 device_addr;
+ u32 extradata_addr;
+ u32 extradata_size;
+ u32 response_required;
+};
+
+struct hfi_frame_data {
+ u32 buffer_type;
+ u32 device_addr;
+ u32 extradata_addr;
+ u64 timestamp;
+ u32 flags;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 mark_target;
+ u32 mark_data;
+ u32 clnt_data;
+ u32 extradata_size;
+};
+
+union hfi_get_property {
+ struct hfi_profile_level profile_level;
+ struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX];
+};
+
+/* HFI events */
+#define EVT_SYS_EVENT_CHANGE 1
+#define EVT_SYS_WATCHDOG_TIMEOUT 2
+#define EVT_SYS_ERROR 3
+#define EVT_SESSION_ERROR 4
+
+/* HFI event callback structure */
+struct hfi_event_data {
+ u32 error;
+ u32 height;
+ u32 width;
+ u32 event_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 tag;
+ u32 profile;
+ u32 level;
+};
+
+/* define core states */
+#define CORE_UNINIT 0
+#define CORE_INIT 1
+
+/* define instance states */
+#define INST_UNINIT 2
+#define INST_INIT 3
+#define INST_LOAD_RESOURCES 4
+#define INST_START 5
+#define INST_STOP 6
+#define INST_RELEASE_RESOURCES 7
+
+struct venus_core;
+struct venus_inst;
+
+struct hfi_core_ops {
+ void (*event_notify)(struct venus_core *core, u32 event);
+};
+
+struct hfi_inst_ops {
+ void (*buf_done)(struct venus_inst *inst, unsigned int buf_type,
+ u32 tag, u32 bytesused, u32 data_offset, u32 flags,
+ u32 hfi_flags, u64 timestamp_us);
+ void (*event_notify)(struct venus_inst *inst, u32 event,
+ struct hfi_event_data *data);
+};
+
+struct hfi_ops {
+ int (*core_init)(struct venus_core *core);
+ int (*core_deinit)(struct venus_core *core);
+ int (*core_ping)(struct venus_core *core, u32 cookie);
+ int (*core_trigger_ssr)(struct venus_core *core, u32 trigger_type);
+
+ int (*session_init)(struct venus_inst *inst, u32 session_type,
+ u32 codec);
+ int (*session_end)(struct venus_inst *inst);
+ int (*session_abort)(struct venus_inst *inst);
+ int (*session_flush)(struct venus_inst *inst, u32 flush_mode);
+ int (*session_start)(struct venus_inst *inst);
+ int (*session_stop)(struct venus_inst *inst);
+ int (*session_continue)(struct venus_inst *inst);
+ int (*session_etb)(struct venus_inst *inst, struct hfi_frame_data *fd);
+ int (*session_ftb)(struct venus_inst *inst, struct hfi_frame_data *fd);
+ int (*session_set_buffers)(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd);
+ int (*session_unset_buffers)(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd);
+ int (*session_load_res)(struct venus_inst *inst);
+ int (*session_release_res)(struct venus_inst *inst);
+ int (*session_parse_seq_hdr)(struct venus_inst *inst, u32 seq_hdr,
+ u32 seq_hdr_len);
+ int (*session_get_seq_hdr)(struct venus_inst *inst, u32 seq_hdr,
+ u32 seq_hdr_len);
+ int (*session_set_property)(struct venus_inst *inst, u32 ptype,
+ void *pdata);
+ int (*session_get_property)(struct venus_inst *inst, u32 ptype);
+
+ int (*resume)(struct venus_core *core);
+ int (*suspend)(struct venus_core *core);
+
+ /* interrupt operations */
+ irqreturn_t (*isr)(struct venus_core *core);
+ irqreturn_t (*isr_thread)(struct venus_core *core);
+};
+
+int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops);
+void hfi_destroy(struct venus_core *core);
+
+int hfi_core_init(struct venus_core *core);
+int hfi_core_deinit(struct venus_core *core, bool blocking);
+int hfi_core_suspend(struct venus_core *core);
+int hfi_core_resume(struct venus_core *core, bool force);
+int hfi_core_trigger_ssr(struct venus_core *core, u32 type);
+int hfi_core_ping(struct venus_core *core);
+int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops);
+void hfi_session_destroy(struct venus_inst *inst);
+int hfi_session_init(struct venus_inst *inst, u32 pixfmt);
+int hfi_session_deinit(struct venus_inst *inst);
+int hfi_session_start(struct venus_inst *inst);
+int hfi_session_stop(struct venus_inst *inst);
+int hfi_session_continue(struct venus_inst *inst);
+int hfi_session_abort(struct venus_inst *inst);
+int hfi_session_load_res(struct venus_inst *inst);
+int hfi_session_unload_res(struct venus_inst *inst);
+int hfi_session_flush(struct venus_inst *inst);
+int hfi_session_set_buffers(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd);
+int hfi_session_unset_buffers(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd);
+int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
+ union hfi_get_property *hprop);
+int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata);
+int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *f);
+irqreturn_t hfi_isr_thread(int irq, void *dev_id);
+irqreturn_t hfi_isr(int irq, void *dev);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
new file mode 100644
index 000000000000..b83c5b8ddccb
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/hash.h>
+
+#include "hfi_cmds.h"
+
+static enum hfi_version hfi_ver;
+
+void pkt_sys_init(struct hfi_sys_init_pkt *pkt, u32 arch_type)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_INIT;
+ pkt->arch_type = arch_type;
+}
+
+void pkt_sys_pc_prep(struct hfi_sys_pc_prep_pkt *pkt)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_PC_PREP;
+}
+
+void pkt_sys_idle_indicator(struct hfi_sys_set_property_pkt *pkt, u32 enable)
+{
+ struct hfi_enable *hfi = (struct hfi_enable *)&pkt->data[1];
+
+ pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_IDLE_INDICATOR;
+ hfi->enable = enable;
+}
+
+void pkt_sys_debug_config(struct hfi_sys_set_property_pkt *pkt, u32 mode,
+ u32 config)
+{
+ struct hfi_debug_config *hfi;
+
+ pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+ hfi = (struct hfi_debug_config *)&pkt->data[1];
+ hfi->config = config;
+ hfi->mode = mode;
+}
+
+void pkt_sys_coverage_config(struct hfi_sys_set_property_pkt *pkt, u32 mode)
+{
+ pkt->hdr.size = sizeof(*pkt) + sizeof(u32);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE;
+ pkt->data[1] = mode;
+}
+
+int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size,
+ u32 addr, void *cookie)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_RESOURCE;
+ pkt->resource_handle = hash32_ptr(cookie);
+
+ switch (id) {
+ case VIDC_RESOURCE_OCMEM:
+ case VIDC_RESOURCE_VMEM: {
+ struct hfi_resource_ocmem *res =
+ (struct hfi_resource_ocmem *)&pkt->resource_data[0];
+
+ res->size = size;
+ res->mem = addr;
+ pkt->resource_type = HFI_RESOURCE_OCMEM;
+ pkt->hdr.size += sizeof(*res) - sizeof(u32);
+ break;
+ }
+ case VIDC_RESOURCE_NONE:
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+int pkt_sys_unset_resource(struct hfi_sys_release_resource_pkt *pkt, u32 id,
+ u32 size, void *cookie)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_RELEASE_RESOURCE;
+ pkt->resource_handle = hash32_ptr(cookie);
+
+ switch (id) {
+ case VIDC_RESOURCE_OCMEM:
+ case VIDC_RESOURCE_VMEM:
+ pkt->resource_type = HFI_RESOURCE_OCMEM;
+ break;
+ case VIDC_RESOURCE_NONE:
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+void pkt_sys_ping(struct hfi_sys_ping_pkt *pkt, u32 cookie)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_PING;
+ pkt->client_data = cookie;
+}
+
+void pkt_sys_power_control(struct hfi_sys_set_property_pkt *pkt, u32 enable)
+{
+ struct hfi_enable *hfi = (struct hfi_enable *)&pkt->data[1];
+
+ pkt->hdr.size = sizeof(*pkt) + sizeof(*hfi) + sizeof(u32);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
+ hfi->enable = enable;
+}
+
+int pkt_sys_ssr_cmd(struct hfi_sys_test_ssr_pkt *pkt, u32 trigger_type)
+{
+ switch (trigger_type) {
+ case HFI_TEST_SSR_SW_ERR_FATAL:
+ case HFI_TEST_SSR_SW_DIV_BY_ZERO:
+ case HFI_TEST_SSR_HW_WDOG_IRQ:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_TEST_SSR;
+ pkt->trigger_type = trigger_type;
+
+ return 0;
+}
+
+void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt)
+{
+ pkt->hdr.size = sizeof(*pkt);
+ pkt->hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION;
+}
+
+int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie,
+ u32 session_type, u32 codec)
+{
+ if (!pkt || !cookie || !codec)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->session_domain = session_type;
+ pkt->session_codec = codec;
+
+ return 0;
+}
+
+void pkt_session_cmd(struct hfi_session_pkt *pkt, u32 pkt_type, void *cookie)
+{
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = pkt_type;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+}
+
+int pkt_session_set_buffers(struct hfi_session_set_buffers_pkt *pkt,
+ void *cookie, struct hfi_buffer_desc *bd)
+{
+ unsigned int i;
+
+ if (!cookie || !pkt || !bd)
+ return -EINVAL;
+
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_BUFFERS;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->buffer_size = bd->buffer_size;
+ pkt->min_buffer_size = bd->buffer_size;
+ pkt->num_buffers = bd->num_buffers;
+
+ if (bd->buffer_type == HFI_BUFFER_OUTPUT ||
+ bd->buffer_type == HFI_BUFFER_OUTPUT2) {
+ struct hfi_buffer_info *bi;
+
+ pkt->extradata_size = bd->extradata_size;
+ pkt->shdr.hdr.size = sizeof(*pkt) - sizeof(u32) +
+ (bd->num_buffers * sizeof(*bi));
+ bi = (struct hfi_buffer_info *)pkt->buffer_info;
+ for (i = 0; i < pkt->num_buffers; i++) {
+ bi->buffer_addr = bd->device_addr;
+ bi->extradata_addr = bd->extradata_addr;
+ }
+ } else {
+ pkt->extradata_size = 0;
+ pkt->shdr.hdr.size = sizeof(*pkt) +
+ ((bd->num_buffers - 1) * sizeof(u32));
+ for (i = 0; i < pkt->num_buffers; i++)
+ pkt->buffer_info[i] = bd->device_addr;
+ }
+
+ pkt->buffer_type = bd->buffer_type;
+
+ return 0;
+}
+
+int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt,
+ void *cookie, struct hfi_buffer_desc *bd)
+{
+ unsigned int i;
+
+ if (!cookie || !pkt || !bd)
+ return -EINVAL;
+
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_RELEASE_BUFFERS;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->buffer_size = bd->buffer_size;
+ pkt->num_buffers = bd->num_buffers;
+
+ if (bd->buffer_type == HFI_BUFFER_OUTPUT ||
+ bd->buffer_type == HFI_BUFFER_OUTPUT2) {
+ struct hfi_buffer_info *bi;
+
+ bi = (struct hfi_buffer_info *)pkt->buffer_info;
+ for (i = 0; i < pkt->num_buffers; i++) {
+ bi->buffer_addr = bd->device_addr;
+ bi->extradata_addr = bd->extradata_addr;
+ }
+ pkt->shdr.hdr.size =
+ sizeof(struct hfi_session_set_buffers_pkt) -
+ sizeof(u32) + (bd->num_buffers * sizeof(*bi));
+ } else {
+ for (i = 0; i < pkt->num_buffers; i++)
+ pkt->buffer_info[i] = bd->device_addr;
+
+ pkt->extradata_size = 0;
+ pkt->shdr.hdr.size =
+ sizeof(struct hfi_session_set_buffers_pkt) +
+ ((bd->num_buffers - 1) * sizeof(u32));
+ }
+
+ pkt->response_req = bd->response_required;
+ pkt->buffer_type = bd->buffer_type;
+
+ return 0;
+}
+
+int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt,
+ void *cookie, struct hfi_frame_data *in_frame)
+{
+ if (!cookie || !in_frame->device_addr)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->time_stamp_hi = upper_32_bits(in_frame->timestamp);
+ pkt->time_stamp_lo = lower_32_bits(in_frame->timestamp);
+ pkt->flags = in_frame->flags;
+ pkt->mark_target = in_frame->mark_target;
+ pkt->mark_data = in_frame->mark_data;
+ pkt->offset = in_frame->offset;
+ pkt->alloc_len = in_frame->alloc_len;
+ pkt->filled_len = in_frame->filled_len;
+ pkt->input_tag = in_frame->clnt_data;
+ pkt->packet_buffer = in_frame->device_addr;
+
+ return 0;
+}
+
+int pkt_session_etb_encoder(
+ struct hfi_session_empty_buffer_uncompressed_plane0_pkt *pkt,
+ void *cookie, struct hfi_frame_data *in_frame)
+{
+ if (!cookie || !in_frame->device_addr)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->view_id = 0;
+ pkt->time_stamp_hi = upper_32_bits(in_frame->timestamp);
+ pkt->time_stamp_lo = lower_32_bits(in_frame->timestamp);
+ pkt->flags = in_frame->flags;
+ pkt->mark_target = in_frame->mark_target;
+ pkt->mark_data = in_frame->mark_data;
+ pkt->offset = in_frame->offset;
+ pkt->alloc_len = in_frame->alloc_len;
+ pkt->filled_len = in_frame->filled_len;
+ pkt->input_tag = in_frame->clnt_data;
+ pkt->packet_buffer = in_frame->device_addr;
+ pkt->extradata_buffer = in_frame->extradata_addr;
+
+ return 0;
+}
+
+int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, void *cookie,
+ struct hfi_frame_data *out_frame)
+{
+ if (!cookie || !out_frame || !out_frame->device_addr)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_FILL_BUFFER;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+
+ if (out_frame->buffer_type == HFI_BUFFER_OUTPUT)
+ pkt->stream_id = 0;
+ else if (out_frame->buffer_type == HFI_BUFFER_OUTPUT2)
+ pkt->stream_id = 1;
+
+ pkt->output_tag = out_frame->clnt_data;
+ pkt->packet_buffer = out_frame->device_addr;
+ pkt->extradata_buffer = out_frame->extradata_addr;
+ pkt->alloc_len = out_frame->alloc_len;
+ pkt->filled_len = out_frame->filled_len;
+ pkt->offset = out_frame->offset;
+ pkt->data[0] = out_frame->extradata_size;
+
+ return 0;
+}
+
+int pkt_session_parse_seq_header(
+ struct hfi_session_parse_sequence_header_pkt *pkt,
+ void *cookie, u32 seq_hdr, u32 seq_hdr_len)
+{
+ if (!cookie || !seq_hdr || !seq_hdr_len)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->header_len = seq_hdr_len;
+ pkt->packet_buffer = seq_hdr;
+
+ return 0;
+}
+
+int pkt_session_get_seq_hdr(struct hfi_session_get_sequence_header_pkt *pkt,
+ void *cookie, u32 seq_hdr, u32 seq_hdr_len)
+{
+ if (!cookie || !seq_hdr || !seq_hdr_len)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->buffer_len = seq_hdr_len;
+ pkt->packet_buffer = seq_hdr;
+
+ return 0;
+}
+
+int pkt_session_flush(struct hfi_session_flush_pkt *pkt, void *cookie, u32 type)
+{
+ switch (type) {
+ case HFI_FLUSH_INPUT:
+ case HFI_FLUSH_OUTPUT:
+ case HFI_FLUSH_OUTPUT2:
+ case HFI_FLUSH_ALL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->flush_type = type;
+
+ return 0;
+}
+
+static int pkt_session_get_property_1x(struct hfi_session_get_property_pkt *pkt,
+ void *cookie, u32 ptype)
+{
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+ pkt->data[0] = ptype;
+
+ return 0;
+}
+
+static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata)
+{
+ void *prop_data;
+ int ret = 0;
+
+ if (!pkt || !cookie || !pdata)
+ return -EINVAL;
+
+ prop_data = &pkt->data[1];
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+
+ switch (ptype) {
+ case HFI_PROPERTY_CONFIG_FRAME_RATE: {
+ struct hfi_framerate *in = pdata, *frate = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE;
+ frate->buffer_type = in->buffer_type;
+ frate->framerate = in->framerate;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*frate);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: {
+ struct hfi_uncompressed_format_select *in = pdata;
+ struct hfi_uncompressed_format_select *hfi = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+ hfi->buffer_type = in->buffer_type;
+ hfi->format = in->format;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_FRAME_SIZE: {
+ struct hfi_framesize *in = pdata, *fsize = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE;
+ fsize->buffer_type = in->buffer_type;
+ fsize->height = in->height;
+ fsize->width = in->width;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*fsize);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_REALTIME: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_REALTIME;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: {
+ struct hfi_buffer_count_actual *in = pdata, *count = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+ count->count_actual = in->count_actual;
+ count->type = in->type;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: {
+ struct hfi_buffer_size_actual *in = pdata, *sz = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
+ sz->size = in->size;
+ sz->type = in->type;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*sz);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL: {
+ struct hfi_buffer_display_hold_count_actual *in = pdata;
+ struct hfi_buffer_display_hold_count_actual *count = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL;
+ count->hold_count = in->hold_count;
+ count->type = in->type;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: {
+ struct hfi_nal_stream_format_select *in = pdata;
+ struct hfi_nal_stream_format_select *fmt = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT;
+ fmt->format = in->format;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*fmt);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER: {
+ u32 *in = pdata;
+
+ switch (*in) {
+ case HFI_OUTPUT_ORDER_DECODE:
+ case HFI_OUTPUT_ORDER_DISPLAY:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE: {
+ struct hfi_enable_picture *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE;
+ en->picture_type = in->picture_type;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: {
+ struct hfi_enable *in = pdata;
+ struct hfi_enable *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: {
+ struct hfi_multi_stream *in = pdata, *multi = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+ multi->buffer_type = in->buffer_type;
+ multi->enable = in->enable;
+ multi->width = in->width;
+ multi->height = in->height;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT: {
+ struct hfi_display_picture_buffer_count *in = pdata;
+ struct hfi_display_picture_buffer_count *count = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT;
+ count->count = in->count;
+ count->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_DIVX_FORMAT: {
+ u32 *in = pdata;
+
+ switch (*in) {
+ case HFI_DIVX_FORMAT_4:
+ case HFI_DIVX_FORMAT_5:
+ case HFI_DIVX_FORMAT_6:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME:
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME;
+ pkt->shdr.hdr.size += sizeof(u32);
+ break;
+ case HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER:
+ break;
+ case HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION:
+ break;
+ case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: {
+ struct hfi_bitrate *in = pdata, *brate = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+ brate->bitrate = in->bitrate;
+ brate->layer_id = in->layer_id;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*brate);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: {
+ struct hfi_bitrate *in = pdata, *hfi = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
+ hfi->bitrate = in->bitrate;
+ hfi->layer_id = in->layer_id;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: {
+ struct hfi_profile_level *in = pdata, *pl = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ pl->level = in->level;
+ pl->profile = in->profile;
+ if (pl->profile <= 0)
+ /* Profile not supported, falling back to high */
+ pl->profile = HFI_H264_PROFILE_HIGH;
+
+ if (!pl->level)
+ /* Level not supported, falling back to 1 */
+ pl->level = 1;
+
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*pl);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: {
+ struct hfi_h264_entropy_control *in = pdata, *hfi = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL;
+ hfi->entropy_mode = in->entropy_mode;
+ if (hfi->entropy_mode == HFI_H264_ENTROPY_CABAC)
+ hfi->cabac_model = in->cabac_model;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: {
+ u32 *in = pdata;
+
+ switch (*in) {
+ case HFI_RATE_CONTROL_OFF:
+ case HFI_RATE_CONTROL_CBR_CFR:
+ case HFI_RATE_CONTROL_CBR_VFR:
+ case HFI_RATE_CONTROL_VBR_CFR:
+ case HFI_RATE_CONTROL_VBR_VFR:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION: {
+ struct hfi_mpeg4_time_resolution *in = pdata, *res = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION;
+ res->time_increment_resolution = in->time_increment_resolution;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*res);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION: {
+ struct hfi_mpeg4_header_extension *in = pdata, *ext = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION;
+ ext->header_extension = in->header_extension;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ext);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL: {
+ struct hfi_h264_db_control *in = pdata, *db = prop_data;
+
+ switch (in->mode) {
+ case HFI_H264_DB_MODE_DISABLE:
+ case HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY:
+ case HFI_H264_DB_MODE_ALL_BOUNDARY:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL;
+ db->mode = in->mode;
+ db->slice_alpha_offset = in->slice_alpha_offset;
+ db->slice_beta_offset = in->slice_beta_offset;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*db);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_SESSION_QP: {
+ struct hfi_quantization *in = pdata, *quant = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SESSION_QP;
+ quant->qp_i = in->qp_i;
+ quant->qp_p = in->qp_p;
+ quant->qp_b = in->qp_b;
+ quant->layer_id = in->layer_id;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*quant);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE: {
+ struct hfi_quantization_range *in = pdata, *range = prop_data;
+ u32 min_qp, max_qp;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE;
+ min_qp = in->min_qp;
+ max_qp = in->max_qp;
+
+ /* We'll be packing in the qp, so make sure we
+ * won't be losing data when masking
+ */
+ if (min_qp > 0xff || max_qp > 0xff) {
+ ret = -ERANGE;
+ break;
+ }
+
+ /* When creating the packet, pack the qp value as
+ * 0xiippbb, where ii = qp range for I-frames,
+ * pp = qp range for P-frames, etc.
+ */
+ range->min_qp = min_qp | min_qp << 8 | min_qp << 16;
+ range->max_qp = max_qp | max_qp << 8 | max_qp << 16;
+ range->layer_id = in->layer_id;
+
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*range);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG: {
+ struct hfi_vc1e_perf_cfg_type *in = pdata, *perf = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG;
+
+ memcpy(perf->search_range_x_subsampled,
+ in->search_range_x_subsampled,
+ sizeof(perf->search_range_x_subsampled));
+ memcpy(perf->search_range_y_subsampled,
+ in->search_range_y_subsampled,
+ sizeof(perf->search_range_y_subsampled));
+
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*perf);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES: {
+ struct hfi_max_num_b_frames *bframes = prop_data;
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES;
+ bframes->max_num_b_frames = *in;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*bframes);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: {
+ struct hfi_intra_period *in = pdata, *intra = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD;
+ intra->pframes = in->pframes;
+ intra->bframes = in->bframes;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD: {
+ struct hfi_idr_period *in = pdata, *idr = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
+ idr->idr_period = in->idr_period;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*idr);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR: {
+ struct hfi_conceal_color *color = prop_data;
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR;
+ color->conceal_color = *in;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VPE_OPERATIONS: {
+ struct hfi_operations_type *in = pdata, *ops = prop_data;
+
+ switch (in->rotation) {
+ case HFI_ROTATE_NONE:
+ case HFI_ROTATE_90:
+ case HFI_ROTATE_180:
+ case HFI_ROTATE_270:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ switch (in->flip) {
+ case HFI_FLIP_NONE:
+ case HFI_FLIP_HORIZONTAL:
+ case HFI_FLIP_VERTICAL:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VPE_OPERATIONS;
+ ops->rotation = in->rotation;
+ ops->flip = in->flip;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ops);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
+ struct hfi_intra_refresh *in = pdata, *intra = prop_data;
+
+ switch (in->mode) {
+ case HFI_INTRA_REFRESH_NONE:
+ case HFI_INTRA_REFRESH_ADAPTIVE:
+ case HFI_INTRA_REFRESH_CYCLIC:
+ case HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+ case HFI_INTRA_REFRESH_RANDOM:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+ intra->mode = in->mode;
+ intra->air_mbs = in->air_mbs;
+ intra->air_ref = in->air_ref;
+ intra->cir_mbs = in->cir_mbs;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL: {
+ struct hfi_multi_slice_control *in = pdata, *multi = prop_data;
+
+ switch (in->multi_slice) {
+ case HFI_MULTI_SLICE_OFF:
+ case HFI_MULTI_SLICE_GOB:
+ case HFI_MULTI_SLICE_BY_MB_COUNT:
+ case HFI_MULTI_SLICE_BY_BYTE_COUNT:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL;
+ multi->multi_slice = in->multi_slice;
+ multi->slice_size = in->slice_size;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO: {
+ struct hfi_h264_vui_timing_info *in = pdata, *vui = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO;
+ vui->enable = in->enable;
+ vui->fixed_framerate = in->fixed_framerate;
+ vui->time_scale = in->time_scale;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*vui);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VPE_DEINTERLACE: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VPE_DEINTERLACE;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: {
+ struct hfi_buffer_alloc_mode *in = pdata, *mode = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
+ mode->type = in->type;
+ mode->mode = in->mode;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*mode);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD: {
+ struct hfi_scs_threshold *thres = prop_data;
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD;
+ thres->threshold_value = *in;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*thres);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT: {
+ struct hfi_mvc_buffer_layout_descp_type *in = pdata;
+ struct hfi_mvc_buffer_layout_descp_type *mvc = prop_data;
+
+ switch (in->layout_type) {
+ case HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM:
+ case HFI_MVC_BUFFER_LAYOUT_SEQ:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT;
+ mvc->layout_type = in->layout_type;
+ mvc->bright_view_first = in->bright_view_first;
+ mvc->ngap = in->ngap;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*mvc);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_LTRMODE: {
+ struct hfi_ltr_mode *in = pdata, *ltr = prop_data;
+
+ switch (in->ltr_mode) {
+ case HFI_LTR_MODE_DISABLE:
+ case HFI_LTR_MODE_MANUAL:
+ case HFI_LTR_MODE_PERIODIC:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_LTRMODE;
+ ltr->ltr_mode = in->ltr_mode;
+ ltr->ltr_count = in->ltr_count;
+ ltr->trust_mode = in->trust_mode;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: {
+ struct hfi_ltr_use *in = pdata, *ltr_use = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME;
+ ltr_use->frames = in->frames;
+ ltr_use->ref_ltr = in->ref_ltr;
+ ltr_use->use_constrnt = in->use_constrnt;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: {
+ struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME;
+ ltr_mark->mark_frame = in->mark_frame;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: {
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: {
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_INITIAL_QP: {
+ struct hfi_initial_quantization *in = pdata, *quant = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INITIAL_QP;
+ quant->init_qp_enable = in->init_qp_enable;
+ quant->qp_i = in->qp_i;
+ quant->qp_p = in->qp_p;
+ quant->qp_b = in->qp_b;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*quant);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION: {
+ struct hfi_vpe_color_space_conversion *in = pdata;
+ struct hfi_vpe_color_space_conversion *csc = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION;
+ memcpy(csc->csc_matrix, in->csc_matrix,
+ sizeof(csc->csc_matrix));
+ memcpy(csc->csc_bias, in->csc_bias, sizeof(csc->csc_bias));
+ memcpy(csc->csc_limit, in->csc_limit, sizeof(csc->csc_limit));
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*csc);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] =
+ HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_PERF_MODE: {
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VENC_PERF_MODE;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER: {
+ u32 *in = pdata;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER;
+ pkt->data[1] = *in;
+ pkt->shdr.hdr.size += sizeof(u32) * 2;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2: {
+ struct hfi_enable *in = pdata, *en = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2;
+ en->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE: {
+ struct hfi_hybrid_hierp *in = pdata, *hierp = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE;
+ hierp->layers = in->layers;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hierp);
+ break;
+ }
+
+ /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ case HFI_PROPERTY_CONFIG_PRIORITY:
+ case HFI_PROPERTY_CONFIG_BATCH_INFO:
+ case HFI_PROPERTY_SYS_IDLE_INDICATOR:
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+ case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED:
+ case HFI_PROPERTY_PARAM_CHROMA_SITE:
+ case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED:
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED:
+ case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED:
+ case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
+ case HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT:
+ case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE:
+ case HFI_PROPERTY_PARAM_CODEC_SUPPORTED:
+ case HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT:
+ case HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION:
+ case HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB:
+ case HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING:
+ case HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO:
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int
+pkt_session_get_property_3xx(struct hfi_session_get_property_pkt *pkt,
+ void *cookie, u32 ptype)
+{
+ int ret = 0;
+
+ if (!pkt || !cookie)
+ return -EINVAL;
+
+ pkt->shdr.hdr.size = sizeof(struct hfi_session_get_property_pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+
+ switch (ptype) {
+ case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+ pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
+ break;
+ default:
+ ret = pkt_session_get_property_1x(pkt, cookie, ptype);
+ break;
+ }
+
+ return ret;
+}
+
+static int
+pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata)
+{
+ void *prop_data;
+ int ret = 0;
+
+ if (!pkt || !cookie || !pdata)
+ return -EINVAL;
+
+ prop_data = &pkt->data[1];
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+
+ /*
+ * Any session set property which is different in 3XX packetization
+ * should be added as a new case below. All unchanged session set
+ * properties will be handled in the default case.
+ */
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: {
+ struct hfi_multi_stream *in = pdata;
+ struct hfi_multi_stream_3x *multi = prop_data;
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+ multi->buffer_type = in->buffer_type;
+ multi->enable = in->enable;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
+ struct hfi_intra_refresh *in = pdata;
+ struct hfi_intra_refresh_3x *intra = prop_data;
+
+ switch (in->mode) {
+ case HFI_INTRA_REFRESH_NONE:
+ case HFI_INTRA_REFRESH_ADAPTIVE:
+ case HFI_INTRA_REFRESH_CYCLIC:
+ case HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+ case HFI_INTRA_REFRESH_RANDOM:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pkt->data[0] = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+ intra->mode = in->mode;
+ intra->mbs = in->cir_mbs;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*intra);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
+ /* for 3xx fw version session_continue is used */
+ break;
+ default:
+ ret = pkt_session_set_property_1x(pkt, cookie, ptype, pdata);
+ break;
+ }
+
+ return ret;
+}
+
+int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt,
+ void *cookie, u32 ptype)
+{
+ if (hfi_ver == HFI_VERSION_1XX)
+ return pkt_session_get_property_1x(pkt, cookie, ptype);
+
+ return pkt_session_get_property_3xx(pkt, cookie, ptype);
+}
+
+int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata)
+{
+ if (hfi_ver == HFI_VERSION_1XX)
+ return pkt_session_set_property_1x(pkt, cookie, ptype, pdata);
+
+ return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata);
+}
+
+void pkt_set_version(enum hfi_version version)
+{
+ hfi_ver = version;
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h
new file mode 100644
index 000000000000..f7617cf59914
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HFI_CMDS_H__
+#define __VENUS_HFI_CMDS_H__
+
+#include "hfi.h"
+
+/* commands */
+#define HFI_CMD_SYS_INIT 0x10001
+#define HFI_CMD_SYS_PC_PREP 0x10002
+#define HFI_CMD_SYS_SET_RESOURCE 0x10003
+#define HFI_CMD_SYS_RELEASE_RESOURCE 0x10004
+#define HFI_CMD_SYS_SET_PROPERTY 0x10005
+#define HFI_CMD_SYS_GET_PROPERTY 0x10006
+#define HFI_CMD_SYS_SESSION_INIT 0x10007
+#define HFI_CMD_SYS_SESSION_END 0x10008
+#define HFI_CMD_SYS_SET_BUFFERS 0x10009
+#define HFI_CMD_SYS_TEST_SSR 0x10101
+
+#define HFI_CMD_SESSION_SET_PROPERTY 0x11001
+#define HFI_CMD_SESSION_SET_BUFFERS 0x11002
+#define HFI_CMD_SESSION_GET_SEQUENCE_HEADER 0x11003
+
+#define HFI_CMD_SYS_SESSION_ABORT 0x210001
+#define HFI_CMD_SYS_PING 0x210002
+
+#define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001
+#define HFI_CMD_SESSION_START 0x211002
+#define HFI_CMD_SESSION_STOP 0x211003
+#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004
+#define HFI_CMD_SESSION_FILL_BUFFER 0x211005
+#define HFI_CMD_SESSION_SUSPEND 0x211006
+#define HFI_CMD_SESSION_RESUME 0x211007
+#define HFI_CMD_SESSION_FLUSH 0x211008
+#define HFI_CMD_SESSION_GET_PROPERTY 0x211009
+#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER 0x21100a
+#define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b
+#define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c
+#define HFI_CMD_SESSION_CONTINUE 0x21100d
+#define HFI_CMD_SESSION_SYNC 0x21100e
+
+/* command packets */
+struct hfi_sys_init_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 arch_type;
+};
+
+struct hfi_sys_pc_prep_pkt {
+ struct hfi_pkt_hdr hdr;
+};
+
+struct hfi_sys_set_resource_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 resource_handle;
+ u32 resource_type;
+ u32 resource_data[1];
+};
+
+struct hfi_sys_release_resource_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 resource_type;
+ u32 resource_handle;
+};
+
+struct hfi_sys_set_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_sys_get_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_sys_set_buffers_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 num_buffers;
+ u32 buffer_addr[1];
+};
+
+struct hfi_sys_ping_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 client_data;
+};
+
+struct hfi_session_init_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 session_domain;
+ u32 session_codec;
+};
+
+struct hfi_session_end_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_abort_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_set_property_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[0];
+};
+
+struct hfi_session_set_buffers_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 min_buffer_size;
+ u32 num_buffers;
+ u32 buffer_info[1];
+};
+
+struct hfi_session_get_sequence_header_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_len;
+ u32 packet_buffer;
+};
+
+struct hfi_session_load_resources_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_start_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_stop_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_empty_buffer_compressed_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[1];
+};
+
+struct hfi_session_empty_buffer_uncompressed_plane0_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 view_id;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[1];
+};
+
+struct hfi_session_empty_buffer_uncompressed_plane1_pkt {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 packet_buffer2;
+ u32 data[1];
+};
+
+struct hfi_session_empty_buffer_uncompressed_plane2_pkt {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 packet_buffer3;
+ u32 data[1];
+};
+
+struct hfi_session_fill_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 output_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[1];
+};
+
+struct hfi_session_flush_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 flush_type;
+};
+
+struct hfi_session_suspend_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_resume_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_get_property_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_session_release_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 response_req;
+ u32 num_buffers;
+ u32 buffer_info[1];
+};
+
+struct hfi_session_release_resources_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_session_parse_sequence_header_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 header_len;
+ u32 packet_buffer;
+};
+
+struct hfi_sfr {
+ u32 buf_size;
+ u8 data[1];
+};
+
+struct hfi_sys_test_ssr_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 trigger_type;
+};
+
+void pkt_set_version(enum hfi_version version);
+
+void pkt_sys_init(struct hfi_sys_init_pkt *pkt, u32 arch_type);
+void pkt_sys_pc_prep(struct hfi_sys_pc_prep_pkt *pkt);
+void pkt_sys_idle_indicator(struct hfi_sys_set_property_pkt *pkt, u32 enable);
+void pkt_sys_power_control(struct hfi_sys_set_property_pkt *pkt, u32 enable);
+int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size,
+ u32 addr, void *cookie);
+int pkt_sys_unset_resource(struct hfi_sys_release_resource_pkt *pkt, u32 id,
+ u32 size, void *cookie);
+void pkt_sys_debug_config(struct hfi_sys_set_property_pkt *pkt, u32 mode,
+ u32 config);
+void pkt_sys_coverage_config(struct hfi_sys_set_property_pkt *pkt, u32 mode);
+void pkt_sys_ping(struct hfi_sys_ping_pkt *pkt, u32 cookie);
+void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt);
+int pkt_sys_ssr_cmd(struct hfi_sys_test_ssr_pkt *pkt, u32 trigger_type);
+int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie,
+ u32 session_type, u32 codec);
+void pkt_session_cmd(struct hfi_session_pkt *pkt, u32 pkt_type, void *cookie);
+int pkt_session_set_buffers(struct hfi_session_set_buffers_pkt *pkt,
+ void *cookie, struct hfi_buffer_desc *bd);
+int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt,
+ void *cookie, struct hfi_buffer_desc *bd);
+int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt,
+ void *cookie, struct hfi_frame_data *input_frame);
+int pkt_session_etb_encoder(
+ struct hfi_session_empty_buffer_uncompressed_plane0_pkt *pkt,
+ void *cookie, struct hfi_frame_data *input_frame);
+int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt,
+ void *cookie, struct hfi_frame_data *output_frame);
+int pkt_session_parse_seq_header(
+ struct hfi_session_parse_sequence_header_pkt *pkt,
+ void *cookie, u32 seq_hdr, u32 seq_hdr_len);
+int pkt_session_get_seq_hdr(struct hfi_session_get_sequence_header_pkt *pkt,
+ void *cookie, u32 seq_hdr, u32 seq_hdr_len);
+int pkt_session_flush(struct hfi_session_flush_pkt *pkt, void *cookie,
+ u32 flush_mode);
+int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt,
+ void *cookie, u32 ptype);
+int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
new file mode 100644
index 000000000000..8d282dba9e57
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HFI_HELPER_H__
+#define __VENUS_HFI_HELPER_H__
+
+#define HFI_DOMAIN_BASE_COMMON 0
+
+#define HFI_DOMAIN_BASE_VDEC 0x1000000
+#define HFI_DOMAIN_BASE_VENC 0x2000000
+#define HFI_DOMAIN_BASE_VPE 0x3000000
+
+#define HFI_VIDEO_ARCH_OX 0x1
+
+#define HFI_ARCH_COMMON_OFFSET 0
+#define HFI_ARCH_OX_OFFSET 0x200000
+
+#define HFI_OX_BASE 0x1000000
+
+#define HFI_CMD_START_OFFSET 0x10000
+#define HFI_MSG_START_OFFSET 0x20000
+
+#define HFI_ERR_NONE 0x0
+#define HFI_ERR_SYS_FATAL 0x1
+#define HFI_ERR_SYS_INVALID_PARAMETER 0x2
+#define HFI_ERR_SYS_VERSION_MISMATCH 0x3
+#define HFI_ERR_SYS_INSUFFICIENT_RESOURCES 0x4
+#define HFI_ERR_SYS_MAX_SESSIONS_REACHED 0x5
+#define HFI_ERR_SYS_UNSUPPORTED_CODEC 0x6
+#define HFI_ERR_SYS_SESSION_IN_USE 0x7
+#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE 0x8
+#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN 0x9
+
+#define HFI_ERR_SESSION_FATAL 0x1001
+#define HFI_ERR_SESSION_INVALID_PARAMETER 0x1002
+#define HFI_ERR_SESSION_BAD_POINTER 0x1003
+#define HFI_ERR_SESSION_INVALID_SESSION_ID 0x1004
+#define HFI_ERR_SESSION_INVALID_STREAM_ID 0x1005
+#define HFI_ERR_SESSION_INCORRECT_STATE_OPERATION 0x1006
+#define HFI_ERR_SESSION_UNSUPPORTED_PROPERTY 0x1007
+#define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008
+#define HFI_ERR_SESSION_INSUFFICIENT_RESOURCES 0x1009
+#define HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED 0x100a
+#define HFI_ERR_SESSION_STREAM_CORRUPT 0x100b
+#define HFI_ERR_SESSION_ENC_OVERFLOW 0x100c
+#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d
+#define HFI_ERR_SESSION_CMDSIZE 0x100e
+#define HFI_ERR_SESSION_UNSUPPORT_CMD 0x100f
+#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010
+#define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL 0x1011
+#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012
+#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013
+
+#define HFI_EVENT_SYS_ERROR 0x1
+#define HFI_EVENT_SESSION_ERROR 0x2
+
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES 0x1000001
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002
+#define HFI_EVENT_SESSION_SEQUENCE_CHANGED 0x1000003
+#define HFI_EVENT_SESSION_PROPERTY_CHANGED 0x1000004
+#define HFI_EVENT_SESSION_LTRUSE_FAILED 0x1000005
+#define HFI_EVENT_RELEASE_BUFFER_REFERENCE 0x1000006
+
+#define HFI_BUFFERFLAG_EOS 0x00000001
+#define HFI_BUFFERFLAG_STARTTIME 0x00000002
+#define HFI_BUFFERFLAG_DECODEONLY 0x00000004
+#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
+#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010
+#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020
+#define HFI_BUFFERFLAG_EXTRADATA 0x00000040
+#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+#define HFI_BUFFERFLAG_READONLY 0x00000200
+#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
+#define HFI_BUFFERFLAG_EOSEQ 0x00200000
+#define HFI_BUFFERFLAG_MBAFF 0x08000000
+#define HFI_BUFFERFLAG_VPE_YUV_601_709_CSC_CLAMP 0x10000000
+#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000
+#define HFI_BUFFERFLAG_TEI 0x40000000
+#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000
+
+#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING 0x1001001
+#define HFI_ERR_SESSION_SAME_STATE_OPERATION 0x1001002
+#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED 0x1001003
+#define HFI_ERR_SESSION_START_CODE_NOT_FOUND 0x1001004
+
+#define HFI_FLUSH_INPUT 0x1000001
+#define HFI_FLUSH_OUTPUT 0x1000002
+#define HFI_FLUSH_OUTPUT2 0x1000003
+#define HFI_FLUSH_ALL 0x1000004
+
+#define HFI_EXTRADATA_NONE 0x00000000
+#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001
+#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002
+#define HFI_EXTRADATA_VC1_FRAMEDISP 0x00000003
+#define HFI_EXTRADATA_VC1_SEQDISP 0x00000004
+#define HFI_EXTRADATA_TIMESTAMP 0x00000005
+#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006
+#define HFI_EXTRADATA_FRAME_RATE 0x00000007
+#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008
+#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009
+#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000d
+#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000e
+#define HFI_EXTRADATA_FRAME_QP 0x0000000f
+#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010
+#define HFI_EXTRADATA_MULTISLICE_INFO 0x7f100000
+#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7f100001
+#define HFI_EXTRADATA_INDEX 0x7f100002
+#define HFI_EXTRADATA_METADATA_LTR 0x7f100004
+#define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002
+
+#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e
+#define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010
+#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003
+
+#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01
+#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02
+#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04
+#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08
+#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10
+
+/*
+ * HFI_PROPERTY_PARAM_OX_START
+ * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000
+ */
+#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL 0x201001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO 0x201002
+#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED 0x201003
+#define HFI_PROPERTY_PARAM_CHROMA_SITE 0x201004
+#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG 0x201005
+#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA 0x201006
+#define HFI_PROPERTY_PARAM_DIVX_FORMAT 0x201007
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE 0x201008
+#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA 0x201009
+#define HFI_PROPERTY_PARAM_ERR_DETECTION_CODE_EXTRADATA 0x20100a
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED 0x20100b
+#define HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL 0x20100c
+#define HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL 0x20100d
+
+/*
+ * HFI_PROPERTY_CONFIG_OX_START
+ * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x2000
+ */
+#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001
+#define HFI_PROPERTY_CONFIG_REALTIME 0x202002
+#define HFI_PROPERTY_CONFIG_PRIORITY 0x202003
+#define HFI_PROPERTY_CONFIG_BATCH_INFO 0x202004
+
+/*
+ * HFI_PROPERTY_PARAM_VDEC_OX_START \
+ * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000
+ */
+#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER 0x1203001
+#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT 0x1203002
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT 0x1203003
+#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE 0x1203004
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER 0x1203005
+#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION 0x1203006
+#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB 0x1203007
+#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING 0x1203008
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO 0x1203009
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA 0x120300a
+#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA 0x120300b
+#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA 0x120300c
+#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE 0x120300d
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY 0x120300e
+#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA 0x1203011
+#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA 0x1203012
+#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA 0x1203013
+#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA 0x1203014
+#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT 0x1203015
+#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA 0x1203016
+#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA 0x1203017
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA 0x1203018
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA 0x1203019
+#define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD 0x120301a
+
+/*
+ * HFI_PROPERTY_CONFIG_VDEC_OX_START
+ * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000
+ */
+#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING 0x1200002
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP 0x1200003
+
+#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004
+
+/*
+ * HFI_PROPERTY_PARAM_VENC_OX_START
+ * HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000
+ */
+#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO 0x2205001
+#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL 0x2205002
+#define HFI_PROPERTY_PARAM_VENC_LTR_INFO 0x2205003
+#define HFI_PROPERTY_PARAM_VENC_MBI_DUMPING 0x2205005
+
+/*
+ * HFI_PROPERTY_CONFIG_VENC_OX_START
+ * HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000
+ */
+#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP 0x2206001
+
+/*
+ * HFI_PROPERTY_PARAM_VPE_OX_START
+ * HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000
+ */
+#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION 0x3207001
+
+#define HFI_PROPERTY_CONFIG_VPE_OX_START \
+ (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000)
+
+#define HFI_CHROMA_SITE_0 0x1000001
+#define HFI_CHROMA_SITE_1 0x1000002
+#define HFI_CHROMA_SITE_2 0x1000003
+#define HFI_CHROMA_SITE_3 0x1000004
+#define HFI_CHROMA_SITE_4 0x1000005
+#define HFI_CHROMA_SITE_5 0x1000006
+
+#define HFI_PRIORITY_LOW 10
+#define HFI_PRIOIRTY_MEDIUM 20
+#define HFI_PRIORITY_HIGH 30
+
+#define HFI_OUTPUT_ORDER_DISPLAY 0x1000001
+#define HFI_OUTPUT_ORDER_DECODE 0x1000002
+
+#define HFI_RATE_CONTROL_OFF 0x1000001
+#define HFI_RATE_CONTROL_VBR_VFR 0x1000002
+#define HFI_RATE_CONTROL_VBR_CFR 0x1000003
+#define HFI_RATE_CONTROL_CBR_VFR 0x1000004
+#define HFI_RATE_CONTROL_CBR_CFR 0x1000005
+
+#define HFI_VIDEO_CODEC_H264 0x00000002
+#define HFI_VIDEO_CODEC_H263 0x00000004
+#define HFI_VIDEO_CODEC_MPEG1 0x00000008
+#define HFI_VIDEO_CODEC_MPEG2 0x00000010
+#define HFI_VIDEO_CODEC_MPEG4 0x00000020
+#define HFI_VIDEO_CODEC_DIVX_311 0x00000040
+#define HFI_VIDEO_CODEC_DIVX 0x00000080
+#define HFI_VIDEO_CODEC_VC1 0x00000100
+#define HFI_VIDEO_CODEC_SPARK 0x00000200
+#define HFI_VIDEO_CODEC_VP8 0x00001000
+#define HFI_VIDEO_CODEC_HEVC 0x00002000
+#define HFI_VIDEO_CODEC_VP9 0x00004000
+#define HFI_VIDEO_CODEC_HEVC_HYBRID 0x80000000
+
+#define HFI_H264_PROFILE_BASELINE 0x00000001
+#define HFI_H264_PROFILE_MAIN 0x00000002
+#define HFI_H264_PROFILE_HIGH 0x00000004
+#define HFI_H264_PROFILE_STEREO_HIGH 0x00000008
+#define HFI_H264_PROFILE_MULTIVIEW_HIGH 0x00000010
+#define HFI_H264_PROFILE_CONSTRAINED_BASE 0x00000020
+#define HFI_H264_PROFILE_CONSTRAINED_HIGH 0x00000040
+
+#define HFI_H264_LEVEL_1 0x00000001
+#define HFI_H264_LEVEL_1b 0x00000002
+#define HFI_H264_LEVEL_11 0x00000004
+#define HFI_H264_LEVEL_12 0x00000008
+#define HFI_H264_LEVEL_13 0x00000010
+#define HFI_H264_LEVEL_2 0x00000020
+#define HFI_H264_LEVEL_21 0x00000040
+#define HFI_H264_LEVEL_22 0x00000080
+#define HFI_H264_LEVEL_3 0x00000100
+#define HFI_H264_LEVEL_31 0x00000200
+#define HFI_H264_LEVEL_32 0x00000400
+#define HFI_H264_LEVEL_4 0x00000800
+#define HFI_H264_LEVEL_41 0x00001000
+#define HFI_H264_LEVEL_42 0x00002000
+#define HFI_H264_LEVEL_5 0x00004000
+#define HFI_H264_LEVEL_51 0x00008000
+#define HFI_H264_LEVEL_52 0x00010000
+
+#define HFI_H263_PROFILE_BASELINE 0x00000001
+
+#define HFI_H263_LEVEL_10 0x00000001
+#define HFI_H263_LEVEL_20 0x00000002
+#define HFI_H263_LEVEL_30 0x00000004
+#define HFI_H263_LEVEL_40 0x00000008
+#define HFI_H263_LEVEL_45 0x00000010
+#define HFI_H263_LEVEL_50 0x00000020
+#define HFI_H263_LEVEL_60 0x00000040
+#define HFI_H263_LEVEL_70 0x00000080
+
+#define HFI_MPEG2_PROFILE_SIMPLE 0x00000001
+#define HFI_MPEG2_PROFILE_MAIN 0x00000002
+#define HFI_MPEG2_PROFILE_422 0x00000004
+#define HFI_MPEG2_PROFILE_SNR 0x00000008
+#define HFI_MPEG2_PROFILE_SPATIAL 0x00000010
+#define HFI_MPEG2_PROFILE_HIGH 0x00000020
+
+#define HFI_MPEG2_LEVEL_LL 0x00000001
+#define HFI_MPEG2_LEVEL_ML 0x00000002
+#define HFI_MPEG2_LEVEL_H14 0x00000004
+#define HFI_MPEG2_LEVEL_HL 0x00000008
+
+#define HFI_MPEG4_PROFILE_SIMPLE 0x00000001
+#define HFI_MPEG4_PROFILE_ADVANCEDSIMPLE 0x00000002
+
+#define HFI_MPEG4_LEVEL_0 0x00000001
+#define HFI_MPEG4_LEVEL_0b 0x00000002
+#define HFI_MPEG4_LEVEL_1 0x00000004
+#define HFI_MPEG4_LEVEL_2 0x00000008
+#define HFI_MPEG4_LEVEL_3 0x00000010
+#define HFI_MPEG4_LEVEL_4 0x00000020
+#define HFI_MPEG4_LEVEL_4a 0x00000040
+#define HFI_MPEG4_LEVEL_5 0x00000080
+#define HFI_MPEG4_LEVEL_6 0x00000100
+#define HFI_MPEG4_LEVEL_7 0x00000200
+#define HFI_MPEG4_LEVEL_8 0x00000400
+#define HFI_MPEG4_LEVEL_9 0x00000800
+#define HFI_MPEG4_LEVEL_3b 0x00001000
+
+#define HFI_VC1_PROFILE_SIMPLE 0x00000001
+#define HFI_VC1_PROFILE_MAIN 0x00000002
+#define HFI_VC1_PROFILE_ADVANCED 0x00000004
+
+#define HFI_VC1_LEVEL_LOW 0x00000001
+#define HFI_VC1_LEVEL_MEDIUM 0x00000002
+#define HFI_VC1_LEVEL_HIGH 0x00000004
+#define HFI_VC1_LEVEL_0 0x00000008
+#define HFI_VC1_LEVEL_1 0x00000010
+#define HFI_VC1_LEVEL_2 0x00000020
+#define HFI_VC1_LEVEL_3 0x00000040
+#define HFI_VC1_LEVEL_4 0x00000080
+
+#define HFI_VPX_PROFILE_SIMPLE 0x00000001
+#define HFI_VPX_PROFILE_ADVANCED 0x00000002
+#define HFI_VPX_PROFILE_VERSION_0 0x00000004
+#define HFI_VPX_PROFILE_VERSION_1 0x00000008
+#define HFI_VPX_PROFILE_VERSION_2 0x00000010
+#define HFI_VPX_PROFILE_VERSION_3 0x00000020
+
+#define HFI_DIVX_FORMAT_4 0x1
+#define HFI_DIVX_FORMAT_5 0x2
+#define HFI_DIVX_FORMAT_6 0x3
+
+#define HFI_DIVX_PROFILE_QMOBILE 0x00000001
+#define HFI_DIVX_PROFILE_MOBILE 0x00000002
+#define HFI_DIVX_PROFILE_MT 0x00000004
+#define HFI_DIVX_PROFILE_HT 0x00000008
+#define HFI_DIVX_PROFILE_HD 0x00000010
+
+#define HFI_HEVC_PROFILE_MAIN 0x00000001
+#define HFI_HEVC_PROFILE_MAIN10 0x00000002
+#define HFI_HEVC_PROFILE_MAIN_STILL_PIC 0x00000004
+
+#define HFI_HEVC_LEVEL_1 0x00000001
+#define HFI_HEVC_LEVEL_2 0x00000002
+#define HFI_HEVC_LEVEL_21 0x00000004
+#define HFI_HEVC_LEVEL_3 0x00000008
+#define HFI_HEVC_LEVEL_31 0x00000010
+#define HFI_HEVC_LEVEL_4 0x00000020
+#define HFI_HEVC_LEVEL_41 0x00000040
+#define HFI_HEVC_LEVEL_5 0x00000080
+#define HFI_HEVC_LEVEL_51 0x00000100
+#define HFI_HEVC_LEVEL_52 0x00000200
+#define HFI_HEVC_LEVEL_6 0x00000400
+#define HFI_HEVC_LEVEL_61 0x00000800
+#define HFI_HEVC_LEVEL_62 0x00001000
+
+#define HFI_HEVC_TIER_MAIN 0x1
+#define HFI_HEVC_TIER_HIGH0 0x2
+
+#define HFI_BUFFER_INPUT 0x1
+#define HFI_BUFFER_OUTPUT 0x2
+#define HFI_BUFFER_OUTPUT2 0x3
+#define HFI_BUFFER_INTERNAL_PERSIST 0x4
+#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
+#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001
+#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002
+#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003
+#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005
+#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006
+
+#define HFI_BUFFER_TYPE_MAX 11
+
+#define HFI_BUFFER_MODE_STATIC 0x1000001
+#define HFI_BUFFER_MODE_RING 0x1000002
+#define HFI_BUFFER_MODE_DYNAMIC 0x1000003
+
+#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1
+#define HFI_VENC_PERFMODE_POWER_SAVE 0x2
+
+/*
+ * HFI_PROPERTY_SYS_COMMON_START
+ * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000
+ */
+#define HFI_PROPERTY_SYS_DEBUG_CONFIG 0x1
+#define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO 0x2
+#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ 0x3
+#define HFI_PROPERTY_SYS_IDLE_INDICATOR 0x4
+#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5
+#define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6
+#define HFI_PROPERTY_SYS_CONFIG_COVERAGE 0x7
+
+/*
+ * HFI_PROPERTY_PARAM_COMMON_START
+ * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000
+ */
+#define HFI_PROPERTY_PARAM_FRAME_SIZE 0x1001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO 0x1002
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT 0x1003
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED 0x1004
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT 0x1005
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED 0x1006
+#define HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED 0x1007
+#define HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED 0x1008
+#define HFI_PROPERTY_PARAM_CODEC_SUPPORTED 0x1009
+#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED 0x100a
+#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT 0x100b
+#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT 0x100c
+#define HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE 0x100d
+#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e
+#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f
+#define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010
+
+/*
+ * HFI_PROPERTY_CONFIG_COMMON_START
+ * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000
+ */
+#define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001
+
+/*
+ * HFI_PROPERTY_PARAM_VDEC_COMMON_START
+ * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x3000
+ */
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001
+#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002
+#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003
+
+/*
+ * HFI_PROPERTY_CONFIG_VDEC_COMMON_START
+ * HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x4000
+ */
+
+/*
+ * HFI_PROPERTY_PARAM_VENC_COMMON_START
+ * HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x5000
+ */
+#define HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE 0x2005001
+#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL 0x2005002
+#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003
+#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004
+#define HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE 0x2005005
+#define HFI_PROPERTY_PARAM_VENC_SESSION_QP 0x2005006
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION 0x2005007
+#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE 0x2005008
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION 0x2005009
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER 0x200500a
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION 0x200500b
+#define HFI_PROPERTY_PARAM_VENC_OPEN_GOP 0x200500c
+#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d
+#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL 0x200500e
+#define HFI_PROPERTY_PARAM_VENC_VBV_HRD_BUF_SIZE 0x200500f
+#define HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED 0x2005010
+#define HFI_PROPERTY_PARAM_VENC_ADVANCED 0x2005012
+#define HFI_PROPERTY_PARAM_VENC_H264_SPS_ID 0x2005014
+#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID 0x2005015
+#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL 0x2005016
+#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO 0x2005017
+#define HFI_PROPERTY_PARAM_VENC_NUMREF 0x2005018
+#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P 0x2005019
+#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT 0x200501b
+#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c
+#define HFI_PROPERTY_PARAM_VENC_VIDEO_FULL_RANGE 0x200501d
+#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO 0x200501e
+#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG 0x200501f
+#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020
+#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC 0x2005021
+#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY 0x2005023
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026
+#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP 0x2005027
+#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP 0x2005028
+#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE 0x2005029
+#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER 0x200502c
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE 0x200502f
+
+/*
+ * HFI_PROPERTY_CONFIG_VENC_COMMON_START
+ * HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000
+ */
+#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001
+#define HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD 0x2006002
+#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003
+#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME 0x2006004
+#define HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE 0x2006005
+#define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE 0x2006007
+#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008
+#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009
+#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a
+#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b
+#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD 0x200600c
+#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE 0x200600e
+
+/*
+ * HFI_PROPERTY_PARAM_VPE_COMMON_START
+ * HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000
+ */
+
+/*
+ * HFI_PROPERTY_CONFIG_VPE_COMMON_START
+ * HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000
+ */
+#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE 0x3008001
+#define HFI_PROPERTY_CONFIG_VPE_OPERATIONS 0x3008002
+
+enum hfi_version {
+ HFI_VERSION_1XX,
+ HFI_VERSION_3XX,
+};
+
+struct hfi_buffer_info {
+ u32 buffer_addr;
+ u32 extradata_addr;
+};
+
+struct hfi_bitrate {
+ u32 bitrate;
+ u32 layer_id;
+};
+
+#define HFI_CAPABILITY_FRAME_WIDTH 0x01
+#define HFI_CAPABILITY_FRAME_HEIGHT 0x02
+#define HFI_CAPABILITY_MBS_PER_FRAME 0x03
+#define HFI_CAPABILITY_MBS_PER_SECOND 0x04
+#define HFI_CAPABILITY_FRAMERATE 0x05
+#define HFI_CAPABILITY_SCALE_X 0x06
+#define HFI_CAPABILITY_SCALE_Y 0x07
+#define HFI_CAPABILITY_BITRATE 0x08
+#define HFI_CAPABILITY_BFRAME 0x09
+#define HFI_CAPABILITY_PEAKBITRATE 0x0a
+#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS 0x10
+#define HFI_CAPABILITY_ENC_LTR_COUNT 0x11
+#define HFI_CAPABILITY_CP_OUTPUT2_THRESH 0x12
+#define HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS 0x13
+#define HFI_CAPABILITY_LCU_SIZE 0x14
+#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15
+#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16
+
+struct hfi_capability {
+ u32 capability_type;
+ u32 min;
+ u32 max;
+ u32 step_size;
+};
+
+struct hfi_capabilities {
+ u32 num_capabilities;
+ struct hfi_capability data[1];
+};
+
+#define HFI_DEBUG_MSG_LOW 0x01
+#define HFI_DEBUG_MSG_MEDIUM 0x02
+#define HFI_DEBUG_MSG_HIGH 0x04
+#define HFI_DEBUG_MSG_ERROR 0x08
+#define HFI_DEBUG_MSG_FATAL 0x10
+#define HFI_DEBUG_MSG_PERF 0x20
+
+#define HFI_DEBUG_MODE_QUEUE 0x01
+#define HFI_DEBUG_MODE_QDSS 0x02
+
+struct hfi_debug_config {
+ u32 config;
+ u32 mode;
+};
+
+struct hfi_enable {
+ u32 enable;
+};
+
+#define HFI_H264_DB_MODE_DISABLE 0x1
+#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY 0x2
+#define HFI_H264_DB_MODE_ALL_BOUNDARY 0x3
+
+struct hfi_h264_db_control {
+ u32 mode;
+ u32 slice_alpha_offset;
+ u32 slice_beta_offset;
+};
+
+#define HFI_H264_ENTROPY_CAVLC 0x1
+#define HFI_H264_ENTROPY_CABAC 0x2
+
+#define HFI_H264_CABAC_MODEL_0 0x1
+#define HFI_H264_CABAC_MODEL_1 0x2
+#define HFI_H264_CABAC_MODEL_2 0x3
+
+struct hfi_h264_entropy_control {
+ u32 entropy_mode;
+ u32 cabac_model;
+};
+
+struct hfi_framerate {
+ u32 buffer_type;
+ u32 framerate;
+};
+
+#define HFI_INTRA_REFRESH_NONE 0x1
+#define HFI_INTRA_REFRESH_CYCLIC 0x2
+#define HFI_INTRA_REFRESH_ADAPTIVE 0x3
+#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4
+#define HFI_INTRA_REFRESH_RANDOM 0x5
+
+struct hfi_intra_refresh {
+ u32 mode;
+ u32 air_mbs;
+ u32 air_ref;
+ u32 cir_mbs;
+};
+
+struct hfi_intra_refresh_3x {
+ u32 mode;
+ u32 mbs;
+};
+
+struct hfi_idr_period {
+ u32 idr_period;
+};
+
+struct hfi_operations_type {
+ u32 rotation;
+ u32 flip;
+};
+
+struct hfi_max_num_b_frames {
+ u32 max_num_b_frames;
+};
+
+struct hfi_vc1e_perf_cfg_type {
+ u32 search_range_x_subsampled[3];
+ u32 search_range_y_subsampled[3];
+};
+
+struct hfi_conceal_color {
+ u32 conceal_color;
+};
+
+struct hfi_intra_period {
+ u32 pframes;
+ u32 bframes;
+};
+
+struct hfi_mpeg4_header_extension {
+ u32 header_extension;
+};
+
+struct hfi_mpeg4_time_resolution {
+ u32 time_increment_resolution;
+};
+
+struct hfi_multi_stream {
+ u32 buffer_type;
+ u32 enable;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_multi_stream_3x {
+ u32 buffer_type;
+ u32 enable;
+};
+
+struct hfi_multi_view_format {
+ u32 views;
+ u32 view_order[1];
+};
+
+#define HFI_MULTI_SLICE_OFF 0x1
+#define HFI_MULTI_SLICE_BY_MB_COUNT 0x2
+#define HFI_MULTI_SLICE_BY_BYTE_COUNT 0x3
+#define HFI_MULTI_SLICE_GOB 0x4
+
+struct hfi_multi_slice_control {
+ u32 multi_slice;
+ u32 slice_size;
+};
+
+#define HFI_NAL_FORMAT_STARTCODES 0x01
+#define HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER 0x02
+#define HFI_NAL_FORMAT_ONE_BYTE_LENGTH 0x04
+#define HFI_NAL_FORMAT_TWO_BYTE_LENGTH 0x08
+#define HFI_NAL_FORMAT_FOUR_BYTE_LENGTH 0x10
+
+struct hfi_nal_stream_format {
+ u32 format;
+};
+
+struct hfi_nal_stream_format_select {
+ u32 format;
+};
+
+#define HFI_PICTURE_TYPE_I 0x01
+#define HFI_PICTURE_TYPE_P 0x02
+#define HFI_PICTURE_TYPE_B 0x04
+#define HFI_PICTURE_TYPE_IDR 0x08
+
+struct hfi_profile_level {
+ u32 profile;
+ u32 level;
+};
+
+#define HFI_MAX_PROFILE_COUNT 16
+
+struct hfi_profile_level_supported {
+ u32 profile_count;
+ struct hfi_profile_level profile_level[1];
+};
+
+struct hfi_quality_vs_speed {
+ u32 quality_vs_speed;
+};
+
+struct hfi_quantization {
+ u32 qp_i;
+ u32 qp_p;
+ u32 qp_b;
+ u32 layer_id;
+};
+
+struct hfi_initial_quantization {
+ u32 qp_i;
+ u32 qp_p;
+ u32 qp_b;
+ u32 init_qp_enable;
+};
+
+struct hfi_quantization_range {
+ u32 min_qp;
+ u32 max_qp;
+ u32 layer_id;
+};
+
+#define HFI_LTR_MODE_DISABLE 0x0
+#define HFI_LTR_MODE_MANUAL 0x1
+#define HFI_LTR_MODE_PERIODIC 0x2
+
+struct hfi_ltr_mode {
+ u32 ltr_mode;
+ u32 ltr_count;
+ u32 trust_mode;
+};
+
+struct hfi_ltr_use {
+ u32 ref_ltr;
+ u32 use_constrnt;
+ u32 frames;
+};
+
+struct hfi_ltr_mark {
+ u32 mark_frame;
+};
+
+struct hfi_framesize {
+ u32 buffer_type;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_h264_vui_timing_info {
+ u32 enable;
+ u32 fixed_framerate;
+ u32 time_scale;
+};
+
+#define HFI_COLOR_FORMAT_MONOCHROME 0x01
+#define HFI_COLOR_FORMAT_NV12 0x02
+#define HFI_COLOR_FORMAT_NV21 0x03
+#define HFI_COLOR_FORMAT_NV12_4x4TILE 0x04
+#define HFI_COLOR_FORMAT_NV21_4x4TILE 0x05
+#define HFI_COLOR_FORMAT_YUYV 0x06
+#define HFI_COLOR_FORMAT_YVYU 0x07
+#define HFI_COLOR_FORMAT_UYVY 0x08
+#define HFI_COLOR_FORMAT_VYUY 0x09
+#define HFI_COLOR_FORMAT_RGB565 0x0a
+#define HFI_COLOR_FORMAT_BGR565 0x0b
+#define HFI_COLOR_FORMAT_RGB888 0x0c
+#define HFI_COLOR_FORMAT_BGR888 0x0d
+#define HFI_COLOR_FORMAT_YUV444 0x0e
+#define HFI_COLOR_FORMAT_RGBA8888 0x10
+
+#define HFI_COLOR_FORMAT_UBWC_BASE 0x8000
+#define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000
+
+#define HFI_COLOR_FORMAT_YUV420_TP10 0x4002
+#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002
+#define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010
+
+struct hfi_uncompressed_format_select {
+ u32 buffer_type;
+ u32 format;
+};
+
+struct hfi_uncompressed_format_supported {
+ u32 buffer_type;
+ u32 format_entries;
+ u32 format_info[1];
+};
+
+struct hfi_uncompressed_plane_actual {
+ int actual_stride;
+ u32 actual_plane_buffer_height;
+};
+
+struct hfi_uncompressed_plane_actual_info {
+ u32 buffer_type;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_actual plane_format[1];
+};
+
+struct hfi_uncompressed_plane_constraints {
+ u32 stride_multiples;
+ u32 max_stride;
+ u32 min_plane_buffer_height_multiple;
+ u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_info {
+ u32 format;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints plane_format[1];
+};
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+ u32 buffer_type;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints plane_format[1];
+};
+
+struct hfi_codec_supported {
+ u32 dec_codecs;
+ u32 enc_codecs;
+};
+
+struct hfi_properties_supported {
+ u32 num_properties;
+ u32 properties[1];
+};
+
+struct hfi_max_sessions_supported {
+ u32 max_sessions;
+};
+
+#define HFI_MAX_MATRIX_COEFFS 9
+#define HFI_MAX_BIAS_COEFFS 3
+#define HFI_MAX_LIMIT_COEFFS 6
+
+struct hfi_vpe_color_space_conversion {
+ u32 csc_matrix[HFI_MAX_MATRIX_COEFFS];
+ u32 csc_bias[HFI_MAX_BIAS_COEFFS];
+ u32 csc_limit[HFI_MAX_LIMIT_COEFFS];
+};
+
+#define HFI_ROTATE_NONE 0x1
+#define HFI_ROTATE_90 0x2
+#define HFI_ROTATE_180 0x3
+#define HFI_ROTATE_270 0x4
+
+#define HFI_FLIP_NONE 0x1
+#define HFI_FLIP_HORIZONTAL 0x2
+#define HFI_FLIP_VERTICAL 0x3
+
+struct hfi_operations {
+ u32 rotate;
+ u32 flip;
+};
+
+#define HFI_RESOURCE_OCMEM 0x1
+
+struct hfi_resource_ocmem {
+ u32 size;
+ u32 mem;
+};
+
+struct hfi_resource_ocmem_requirement {
+ u32 session_domain;
+ u32 width;
+ u32 height;
+ u32 size;
+};
+
+struct hfi_resource_ocmem_requirement_info {
+ u32 num_entries;
+ struct hfi_resource_ocmem_requirement requirements[1];
+};
+
+struct hfi_property_sys_image_version_info_type {
+ u32 string_size;
+ u8 str_image_version[1];
+};
+
+struct hfi_codec_mask_supported {
+ u32 codecs;
+ u32 video_domains;
+};
+
+struct hfi_seq_header_info {
+ u32 max_hader_len;
+};
+
+struct hfi_aspect_ratio {
+ u32 aspect_width;
+ u32 aspect_height;
+};
+
+#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM 0
+#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE 1
+#define HFI_MVC_BUFFER_LAYOUT_SEQ 2
+
+struct hfi_mvc_buffer_layout_descp_type {
+ u32 layout_type;
+ u32 bright_view_first;
+ u32 ngap;
+};
+
+struct hfi_scs_threshold {
+ u32 threshold_value;
+};
+
+#define HFI_TEST_SSR_SW_ERR_FATAL 0x1
+#define HFI_TEST_SSR_SW_DIV_BY_ZERO 0x2
+#define HFI_TEST_SSR_HW_WDOG_IRQ 0x3
+
+struct hfi_buffer_alloc_mode {
+ u32 type;
+ u32 mode;
+};
+
+struct hfi_index_extradata_config {
+ u32 enable;
+ u32 index_extra_data_id;
+};
+
+struct hfi_extradata_header {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 type;
+ u32 data_size;
+ u8 data[1];
+};
+
+struct hfi_batch_info {
+ u32 input_batch_count;
+ u32 output_batch_count;
+};
+
+struct hfi_buffer_count_actual {
+ u32 type;
+ u32 count_actual;
+};
+
+struct hfi_buffer_size_actual {
+ u32 type;
+ u32 size;
+};
+
+struct hfi_buffer_display_hold_count_actual {
+ u32 type;
+ u32 hold_count;
+};
+
+struct hfi_buffer_requirements {
+ u32 type;
+ u32 size;
+ u32 region_size;
+ u32 hold_count;
+ u32 count_min;
+ u32 count_actual;
+ u32 contiguous;
+ u32 alignment;
+};
+
+struct hfi_data_payload {
+ u32 size;
+ u8 data[1];
+};
+
+struct hfi_enable_picture {
+ u32 picture_type;
+};
+
+struct hfi_display_picture_buffer_count {
+ int enable;
+ u32 count;
+};
+
+struct hfi_extra_data_header_config {
+ u32 type;
+ u32 buffer_type;
+ u32 version;
+ u32 port_index;
+ u32 client_extra_data_id;
+};
+
+struct hfi_interlace_format_supported {
+ u32 buffer_type;
+ u32 format;
+};
+
+struct hfi_buffer_alloc_mode_supported {
+ u32 buffer_type;
+ u32 num_entries;
+ u32 data[1];
+};
+
+struct hfi_mb_error_map {
+ u32 error_map_size;
+ u8 error_map[1];
+};
+
+struct hfi_metadata_pass_through {
+ int enable;
+ u32 size;
+};
+
+struct hfi_multi_view_select {
+ u32 view_index;
+};
+
+struct hfi_hybrid_hierp {
+ u32 layers;
+};
+
+struct hfi_pkt_hdr {
+ u32 size;
+ u32 pkt_type;
+};
+
+struct hfi_session_hdr_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 session_id;
+};
+
+struct hfi_session_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
new file mode 100644
index 000000000000..a681ae5381d6
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/hash.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "core.h"
+#include "hfi.h"
+#include "hfi_helper.h"
+#include "hfi_msgs.h"
+
+static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct hfi_event_data event = {0};
+ int num_properties_changed;
+ struct hfi_framesize *frame_sz;
+ struct hfi_profile_level *profile_level;
+ u8 *data_ptr;
+ u32 ptype;
+
+ inst->error = HFI_ERR_NONE;
+
+ switch (pkt->event_data1) {
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
+ break;
+ default:
+ inst->error = HFI_ERR_SESSION_INVALID_PARAMETER;
+ goto done;
+ }
+
+ event.event_type = pkt->event_data1;
+
+ num_properties_changed = pkt->event_data2;
+ if (!num_properties_changed) {
+ inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
+ goto done;
+ }
+
+ data_ptr = (u8 *)&pkt->ext_event_data[0];
+ do {
+ ptype = *((u32 *)data_ptr);
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_FRAME_SIZE:
+ data_ptr += sizeof(u32);
+ frame_sz = (struct hfi_framesize *)data_ptr;
+ event.width = frame_sz->width;
+ event.height = frame_sz->height;
+ data_ptr += sizeof(frame_sz);
+ break;
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+ data_ptr += sizeof(u32);
+ profile_level = (struct hfi_profile_level *)data_ptr;
+ event.profile = profile_level->profile;
+ event.level = profile_level->level;
+ data_ptr += sizeof(profile_level);
+ break;
+ default:
+ break;
+ }
+ num_properties_changed--;
+ } while (num_properties_changed > 0);
+
+done:
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+}
+
+static void event_release_buffer_ref(struct venus_core *core,
+ struct venus_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct hfi_event_data event = {0};
+ struct hfi_msg_event_release_buffer_ref_pkt *data;
+
+ data = (struct hfi_msg_event_release_buffer_ref_pkt *)
+ pkt->ext_event_data;
+
+ event.event_type = HFI_EVENT_RELEASE_BUFFER_REFERENCE;
+ event.packet_buffer = data->packet_buffer;
+ event.extradata_buffer = data->extradata_buffer;
+ event.tag = data->output_tag;
+
+ inst->error = HFI_ERR_NONE;
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+}
+
+static void event_sys_error(struct venus_core *core, u32 event,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ if (pkt)
+ dev_dbg(core->dev,
+ "sys error (session id:%x, data1:%x, data2:%x)\n",
+ pkt->shdr.session_id, pkt->event_data1,
+ pkt->event_data2);
+
+ core->core_ops->event_notify(core, event);
+}
+
+static void
+event_session_error(struct venus_core *core, struct venus_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct device *dev = core->dev;
+
+ dev_dbg(dev, "session error: event id:%x, session id:%x\n",
+ pkt->event_data1, pkt->shdr.session_id);
+
+ if (!inst)
+ return;
+
+ switch (pkt->event_data1) {
+ /* non fatal session errors */
+ case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
+ case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
+ case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+ case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
+ inst->error = HFI_ERR_NONE;
+ break;
+ default:
+ dev_err(dev, "session error: event id:%x (%x), session id:%x\n",
+ pkt->event_data1, pkt->event_data2,
+ pkt->shdr.session_id);
+
+ inst->error = pkt->event_data1;
+ inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
+ break;
+ }
+}
+
+static void hfi_event_notify(struct venus_core *core, struct venus_inst *inst,
+ void *packet)
+{
+ struct hfi_msg_event_notify_pkt *pkt = packet;
+
+ if (!packet)
+ return;
+
+ switch (pkt->event_id) {
+ case HFI_EVENT_SYS_ERROR:
+ event_sys_error(core, EVT_SYS_ERROR, pkt);
+ break;
+ case HFI_EVENT_SESSION_ERROR:
+ event_session_error(core, inst, pkt);
+ break;
+ case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
+ event_seq_changed(core, inst, pkt);
+ break;
+ case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
+ event_release_buffer_ref(core, inst, pkt);
+ break;
+ case HFI_EVENT_SESSION_PROPERTY_CHANGED:
+ break;
+ default:
+ break;
+ }
+}
+
+static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst,
+ void *packet)
+{
+ struct hfi_msg_sys_init_done_pkt *pkt = packet;
+ u32 rem_bytes, read_bytes = 0, num_properties;
+ u32 error, ptype;
+ u8 *data;
+
+ error = pkt->error_type;
+ if (error != HFI_ERR_NONE)
+ goto err_no_prop;
+
+ num_properties = pkt->num_properties;
+
+ if (!num_properties) {
+ error = HFI_ERR_SYS_INVALID_PARAMETER;
+ goto err_no_prop;
+ }
+
+ rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32);
+
+ if (!rem_bytes) {
+ /* missing property data */
+ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+ goto err_no_prop;
+ }
+
+ data = (u8 *)&pkt->data[0];
+
+ if (core->res->hfi_version == HFI_VERSION_3XX)
+ goto err_no_prop;
+
+ while (num_properties && rem_bytes >= sizeof(u32)) {
+ ptype = *((u32 *)data);
+ data += sizeof(u32);
+
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: {
+ struct hfi_codec_supported *prop;
+
+ prop = (struct hfi_codec_supported *)data;
+
+ if (rem_bytes < sizeof(*prop)) {
+ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ read_bytes += sizeof(*prop) + sizeof(u32);
+ core->dec_codecs = prop->dec_codecs;
+ core->enc_codecs = prop->enc_codecs;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: {
+ struct hfi_max_sessions_supported *prop;
+
+ if (rem_bytes < sizeof(*prop)) {
+ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ prop = (struct hfi_max_sessions_supported *)data;
+ read_bytes += sizeof(*prop) + sizeof(u32);
+ core->max_sessions_supported = prop->max_sessions;
+ break;
+ }
+ default:
+ error = HFI_ERR_SYS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (error)
+ break;
+
+ rem_bytes -= read_bytes;
+ data += read_bytes;
+ num_properties--;
+ }
+
+err_no_prop:
+ core->error = error;
+ complete(&core->done);
+}
+
+static void
+sys_get_prop_image_version(struct device *dev,
+ struct hfi_msg_sys_property_info_pkt *pkt)
+{
+ int req_bytes;
+
+ req_bytes = pkt->hdr.size - sizeof(*pkt);
+
+ if (req_bytes < 128 || !pkt->data[1] || pkt->num_properties > 1)
+ /* bad packet */
+ return;
+
+ dev_dbg(dev, "F/W version: %s\n", (u8 *)&pkt->data[1]);
+}
+
+static void hfi_sys_property_info(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_sys_property_info_pkt *pkt = packet;
+ struct device *dev = core->dev;
+
+ if (!pkt->num_properties) {
+ dev_dbg(dev, "%s: no properties\n", __func__);
+ return;
+ }
+
+ switch (pkt->data[0]) {
+ case HFI_PROPERTY_SYS_IMAGE_VERSION:
+ sys_get_prop_image_version(dev, pkt);
+ break;
+ default:
+ dev_dbg(dev, "%s: unknown property data\n", __func__);
+ break;
+ }
+}
+
+static void hfi_sys_rel_resource_done(struct venus_core *core,
+ struct venus_inst *inst,
+ void *packet)
+{
+ struct hfi_msg_sys_release_resource_done_pkt *pkt = packet;
+
+ core->error = pkt->error_type;
+ complete(&core->done);
+}
+
+static void hfi_sys_ping_done(struct venus_core *core, struct venus_inst *inst,
+ void *packet)
+{
+ struct hfi_msg_sys_ping_ack_pkt *pkt = packet;
+
+ core->error = HFI_ERR_NONE;
+
+ if (pkt->client_data != 0xbeef)
+ core->error = HFI_ERR_SYS_FATAL;
+
+ complete(&core->done);
+}
+
+static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst,
+ void *packet)
+{
+ dev_dbg(core->dev, "sys idle\n");
+}
+
+static void hfi_sys_pc_prepare_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet;
+
+ dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type);
+}
+
+static void
+hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst)
+{
+ if (!in || !inst)
+ return;
+
+ switch (in->capability_type) {
+ case HFI_CAPABILITY_FRAME_WIDTH:
+ inst->cap_width = *in;
+ break;
+ case HFI_CAPABILITY_FRAME_HEIGHT:
+ inst->cap_height = *in;
+ break;
+ case HFI_CAPABILITY_MBS_PER_FRAME:
+ inst->cap_mbs_per_frame = *in;
+ break;
+ case HFI_CAPABILITY_MBS_PER_SECOND:
+ inst->cap_mbs_per_sec = *in;
+ break;
+ case HFI_CAPABILITY_FRAMERATE:
+ inst->cap_framerate = *in;
+ break;
+ case HFI_CAPABILITY_SCALE_X:
+ inst->cap_scale_x = *in;
+ break;
+ case HFI_CAPABILITY_SCALE_Y:
+ inst->cap_scale_y = *in;
+ break;
+ case HFI_CAPABILITY_BITRATE:
+ inst->cap_bitrate = *in;
+ break;
+ case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
+ inst->cap_hier_p = *in;
+ break;
+ case HFI_CAPABILITY_ENC_LTR_COUNT:
+ inst->cap_ltr_count = *in;
+ break;
+ case HFI_CAPABILITY_CP_OUTPUT2_THRESH:
+ inst->cap_secure_output2_threshold = *in;
+ break;
+ default:
+ break;
+ }
+}
+
+static unsigned int
+session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt,
+ struct hfi_profile_level *profile_level)
+{
+ struct hfi_profile_level *hfi;
+ u32 req_bytes;
+
+ req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
+
+ if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level))
+ /* bad packet */
+ return HFI_ERR_SESSION_INVALID_PARAMETER;
+
+ hfi = (struct hfi_profile_level *)&pkt->data[1];
+ profile_level->profile = hfi->profile;
+ profile_level->level = hfi->level;
+
+ return HFI_ERR_NONE;
+}
+
+static unsigned int
+session_get_prop_buf_req(struct hfi_msg_session_property_info_pkt *pkt,
+ struct hfi_buffer_requirements *bufreq)
+{
+ struct hfi_buffer_requirements *buf_req;
+ u32 req_bytes;
+ unsigned int idx = 0;
+
+ req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
+
+ if (!req_bytes || req_bytes % sizeof(*buf_req) || !pkt->data[1])
+ /* bad packet */
+ return HFI_ERR_SESSION_INVALID_PARAMETER;
+
+ buf_req = (struct hfi_buffer_requirements *)&pkt->data[1];
+ if (!buf_req)
+ return HFI_ERR_SESSION_INVALID_PARAMETER;
+
+ while (req_bytes) {
+ memcpy(&bufreq[idx], buf_req, sizeof(*bufreq));
+ idx++;
+
+ if (idx > HFI_BUFFER_TYPE_MAX)
+ return HFI_ERR_SESSION_INVALID_PARAMETER;
+
+ req_bytes -= sizeof(struct hfi_buffer_requirements);
+ buf_req++;
+ }
+
+ return HFI_ERR_NONE;
+}
+
+static void hfi_session_prop_info(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_property_info_pkt *pkt = packet;
+ struct device *dev = core->dev;
+ union hfi_get_property *hprop = &inst->hprop;
+ unsigned int error = HFI_ERR_NONE;
+
+ if (!pkt->num_properties) {
+ error = HFI_ERR_SESSION_INVALID_PARAMETER;
+ dev_err(dev, "%s: no properties\n", __func__);
+ goto done;
+ }
+
+ switch (pkt->data[0]) {
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ memset(hprop->bufreq, 0, sizeof(hprop->bufreq));
+ error = session_get_prop_buf_req(pkt, hprop->bufreq);
+ break;
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+ memset(&hprop->profile_level, 0, sizeof(hprop->profile_level));
+ error = session_get_prop_profile_level(pkt,
+ &hprop->profile_level);
+ break;
+ case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+ break;
+ default:
+ dev_dbg(dev, "%s: unknown property id:%x\n", __func__,
+ pkt->data[0]);
+ return;
+ }
+
+done:
+ inst->error = error;
+ complete(&inst->done);
+}
+
+static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst,
+ struct hfi_msg_session_init_done_pkt *pkt)
+{
+ struct device *dev = core->dev;
+ u32 rem_bytes, num_props;
+ u32 ptype, next_offset = 0;
+ u32 err;
+ u8 *data;
+
+ rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32);
+ if (!rem_bytes) {
+ dev_err(dev, "%s: missing property info\n", __func__);
+ return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
+ }
+
+ err = pkt->error_type;
+ if (err)
+ return err;
+
+ data = (u8 *)&pkt->data[0];
+ num_props = pkt->num_properties;
+
+ while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) {
+ ptype = *((u32 *)data);
+ next_offset = sizeof(u32);
+
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: {
+ struct hfi_codec_mask_supported *masks =
+ (struct hfi_codec_mask_supported *)
+ (data + next_offset);
+
+ next_offset += sizeof(*masks);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: {
+ struct hfi_capabilities *caps;
+ struct hfi_capability *cap;
+ u32 num_caps;
+
+ if ((rem_bytes - next_offset) < sizeof(*cap)) {
+ err = HFI_ERR_SESSION_INVALID_PARAMETER;
+ break;
+ }
+
+ caps = (struct hfi_capabilities *)(data + next_offset);
+
+ num_caps = caps->num_capabilities;
+ cap = &caps->data[0];
+ next_offset += sizeof(u32);
+
+ while (num_caps &&
+ (rem_bytes - next_offset) >= sizeof(u32)) {
+ hfi_copy_cap_prop(cap, inst);
+ cap++;
+ next_offset += sizeof(*cap);
+ num_caps--;
+ }
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: {
+ struct hfi_uncompressed_format_supported *prop =
+ (struct hfi_uncompressed_format_supported *)
+ (data + next_offset);
+ u32 num_fmt_entries;
+ u8 *fmt;
+ struct hfi_uncompressed_plane_info *inf;
+
+ if ((rem_bytes - next_offset) < sizeof(*prop)) {
+ err = HFI_ERR_SESSION_INVALID_PARAMETER;
+ break;
+ }
+
+ num_fmt_entries = prop->format_entries;
+ next_offset = sizeof(*prop) - sizeof(u32);
+ fmt = (u8 *)&prop->format_info[0];
+
+ dev_dbg(dev, "uncomm format support num entries:%u\n",
+ num_fmt_entries);
+
+ while (num_fmt_entries) {
+ struct hfi_uncompressed_plane_constraints *cnts;
+ u32 bytes_to_skip;
+
+ inf = (struct hfi_uncompressed_plane_info *)fmt;
+
+ if ((rem_bytes - next_offset) < sizeof(*inf)) {
+ err = HFI_ERR_SESSION_INVALID_PARAMETER;
+ break;
+ }
+
+ dev_dbg(dev, "plane info: fmt:%x, planes:%x\n",
+ inf->format, inf->num_planes);
+
+ cnts = &inf->plane_format[0];
+ dev_dbg(dev, "%u %u %u %u\n",
+ cnts->stride_multiples,
+ cnts->max_stride,
+ cnts->min_plane_buffer_height_multiple,
+ cnts->buffer_alignment);
+
+ bytes_to_skip = sizeof(*inf) - sizeof(*cnts) +
+ inf->num_planes * sizeof(*cnts);
+
+ fmt += bytes_to_skip;
+ next_offset += bytes_to_skip;
+ num_fmt_entries--;
+ }
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: {
+ struct hfi_properties_supported *prop =
+ (struct hfi_properties_supported *)
+ (data + next_offset);
+
+ next_offset += sizeof(*prop) - sizeof(u32)
+ + prop->num_properties * sizeof(u32);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: {
+ struct hfi_profile_level_supported *prop =
+ (struct hfi_profile_level_supported *)
+ (data + next_offset);
+ struct hfi_profile_level *pl;
+ unsigned int prop_count = 0;
+ unsigned int count = 0;
+ u8 *ptr;
+
+ ptr = (u8 *)&prop->profile_level[0];
+ prop_count = prop->profile_count;
+
+ if (prop_count > HFI_MAX_PROFILE_COUNT)
+ prop_count = HFI_MAX_PROFILE_COUNT;
+
+ while (prop_count) {
+ ptr++;
+ pl = (struct hfi_profile_level *)ptr;
+
+ inst->pl[count].profile = pl->profile;
+ inst->pl[count].level = pl->level;
+ prop_count--;
+ count++;
+ ptr += sizeof(*pl) / sizeof(u32);
+ }
+
+ inst->pl_count = count;
+ next_offset += sizeof(*prop) - sizeof(*pl) +
+ prop->profile_count * sizeof(*pl);
+
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: {
+ next_offset +=
+ sizeof(struct hfi_interlace_format_supported);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: {
+ struct hfi_nal_stream_format *nal =
+ (struct hfi_nal_stream_format *)
+ (data + next_offset);
+ dev_dbg(dev, "NAL format: %x\n", nal->format);
+ next_offset += sizeof(*nal);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: {
+ next_offset += sizeof(u32);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: {
+ u32 *max_seq_sz = (u32 *)(data + next_offset);
+
+ dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz);
+ next_offset += sizeof(u32);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
+ next_offset += sizeof(struct hfi_intra_refresh);
+ num_props--;
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: {
+ struct hfi_buffer_alloc_mode_supported *prop =
+ (struct hfi_buffer_alloc_mode_supported *)
+ (data + next_offset);
+ unsigned int i;
+
+ for (i = 0; i < prop->num_entries; i++) {
+ if (prop->buffer_type == HFI_BUFFER_OUTPUT ||
+ prop->buffer_type == HFI_BUFFER_OUTPUT2) {
+ switch (prop->data[i]) {
+ case HFI_BUFFER_MODE_STATIC:
+ inst->cap_bufs_mode_static = 1;
+ break;
+ case HFI_BUFFER_MODE_DYNAMIC:
+ inst->cap_bufs_mode_dynamic = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ next_offset += sizeof(*prop) -
+ sizeof(u32) + prop->num_entries * sizeof(u32);
+ num_props--;
+ break;
+ }
+ default:
+ dev_dbg(dev, "%s: default case %#x\n", __func__, ptype);
+ break;
+ }
+
+ rem_bytes -= next_offset;
+ data += next_offset;
+ }
+
+ return err;
+}
+
+static void hfi_session_init_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_init_done_pkt *pkt = packet;
+ unsigned int error;
+
+ error = pkt->error_type;
+ if (error != HFI_ERR_NONE)
+ goto done;
+
+ if (core->res->hfi_version != HFI_VERSION_1XX)
+ goto done;
+
+ error = init_done_read_prop(core, inst, pkt);
+
+done:
+ inst->error = error;
+ complete(&inst->done);
+}
+
+static void hfi_session_load_res_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_load_resources_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_flush_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_flush_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_etb_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ inst->ops->buf_done(inst, HFI_BUFFER_INPUT, pkt->input_tag,
+ pkt->filled_len, pkt->offset, 0, 0, 0);
+}
+
+static void hfi_session_ftb_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ u32 session_type = inst->session_type;
+ u64 timestamp_us = 0;
+ u32 timestamp_hi = 0, timestamp_lo = 0;
+ unsigned int error;
+ u32 flags = 0, hfi_flags = 0, offset = 0, filled_len = 0;
+ u32 pic_type = 0, buffer_type = 0, output_tag = -1;
+
+ if (session_type == VIDC_SESSION_TYPE_ENC) {
+ struct hfi_msg_session_fbd_compressed_pkt *pkt = packet;
+
+ timestamp_hi = pkt->time_stamp_hi;
+ timestamp_lo = pkt->time_stamp_lo;
+ hfi_flags = pkt->flags;
+ offset = pkt->offset;
+ filled_len = pkt->filled_len;
+ pic_type = pkt->picture_type;
+ output_tag = pkt->output_tag;
+ buffer_type = HFI_BUFFER_OUTPUT;
+
+ error = pkt->error_type;
+ } else if (session_type == VIDC_SESSION_TYPE_DEC) {
+ struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt =
+ packet;
+
+ timestamp_hi = pkt->time_stamp_hi;
+ timestamp_lo = pkt->time_stamp_lo;
+ hfi_flags = pkt->flags;
+ offset = pkt->offset;
+ filled_len = pkt->filled_len;
+ pic_type = pkt->picture_type;
+ output_tag = pkt->output_tag;
+
+ if (pkt->stream_id == 0)
+ buffer_type = HFI_BUFFER_OUTPUT;
+ else if (pkt->stream_id == 1)
+ buffer_type = HFI_BUFFER_OUTPUT2;
+
+ error = pkt->error_type;
+ } else {
+ error = HFI_ERR_SESSION_INVALID_PARAMETER;
+ }
+
+ if (buffer_type != HFI_BUFFER_OUTPUT)
+ goto done;
+
+ if (hfi_flags & HFI_BUFFERFLAG_EOS)
+ flags |= V4L2_BUF_FLAG_LAST;
+
+ switch (pic_type) {
+ case HFI_PICTURE_IDR:
+ case HFI_PICTURE_I:
+ flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case HFI_PICTURE_P:
+ flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case HFI_PICTURE_B:
+ flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ case HFI_FRAME_NOTCODED:
+ case HFI_UNUSED_PICT:
+ case HFI_FRAME_YUV:
+ default:
+ break;
+ }
+
+ if (!(hfi_flags & HFI_BUFFERFLAG_TIMESTAMPINVALID) && filled_len) {
+ timestamp_us = timestamp_hi;
+ timestamp_us = (timestamp_us << 32) | timestamp_lo;
+ }
+
+done:
+ inst->error = error;
+ inst->ops->buf_done(inst, buffer_type, output_tag, filled_len,
+ offset, flags, hfi_flags, timestamp_us);
+}
+
+static void hfi_session_start_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_start_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_stop_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_stop_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_rel_res_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_release_resources_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_rel_buf_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_release_buffers_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_end_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_end_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_abort_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_sys_session_abort_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+static void hfi_session_get_seq_hdr_done(struct venus_core *core,
+ struct venus_inst *inst, void *packet)
+{
+ struct hfi_msg_session_get_sequence_hdr_done_pkt *pkt = packet;
+
+ inst->error = pkt->error_type;
+ complete(&inst->done);
+}
+
+struct hfi_done_handler {
+ u32 pkt;
+ u32 pkt_sz;
+ u32 pkt_sz2;
+ void (*done)(struct venus_core *, struct venus_inst *, void *);
+ bool is_sys_pkt;
+};
+
+static const struct hfi_done_handler handlers[] = {
+ {.pkt = HFI_MSG_EVENT_NOTIFY,
+ .pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
+ .done = hfi_event_notify,
+ },
+ {.pkt = HFI_MSG_SYS_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
+ .done = hfi_sys_init_done,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_PROPERTY_INFO,
+ .pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
+ .done = hfi_sys_property_info,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_RELEASE_RESOURCE,
+ .pkt_sz = sizeof(struct hfi_msg_sys_release_resource_done_pkt),
+ .done = hfi_sys_rel_resource_done,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_PING_ACK,
+ .pkt_sz = sizeof(struct hfi_msg_sys_ping_ack_pkt),
+ .done = hfi_sys_ping_done,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_IDLE,
+ .pkt_sz = sizeof(struct hfi_msg_sys_idle_pkt),
+ .done = hfi_sys_idle_done,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_PC_PREP,
+ .pkt_sz = sizeof(struct hfi_msg_sys_pc_prep_done_pkt),
+ .done = hfi_sys_pc_prepare_done,
+ .is_sys_pkt = true,
+ },
+ {.pkt = HFI_MSG_SYS_SESSION_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
+ .done = hfi_session_init_done,
+ },
+ {.pkt = HFI_MSG_SYS_SESSION_END,
+ .pkt_sz = sizeof(struct hfi_msg_session_end_done_pkt),
+ .done = hfi_session_end_done,
+ },
+ {.pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_load_resources_done_pkt),
+ .done = hfi_session_load_res_done,
+ },
+ {.pkt = HFI_MSG_SESSION_START,
+ .pkt_sz = sizeof(struct hfi_msg_session_start_done_pkt),
+ .done = hfi_session_start_done,
+ },
+ {.pkt = HFI_MSG_SESSION_STOP,
+ .pkt_sz = sizeof(struct hfi_msg_session_stop_done_pkt),
+ .done = hfi_session_stop_done,
+ },
+ {.pkt = HFI_MSG_SYS_SESSION_ABORT,
+ .pkt_sz = sizeof(struct hfi_msg_sys_session_abort_done_pkt),
+ .done = hfi_session_abort_done,
+ },
+ {.pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
+ .done = hfi_session_etb_done,
+ },
+ {.pkt = HFI_MSG_SESSION_FILL_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
+ .pkt_sz2 = sizeof(struct hfi_msg_session_fbd_compressed_pkt),
+ .done = hfi_session_ftb_done,
+ },
+ {.pkt = HFI_MSG_SESSION_FLUSH,
+ .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
+ .done = hfi_session_flush_done,
+ },
+ {.pkt = HFI_MSG_SESSION_PROPERTY_INFO,
+ .pkt_sz = sizeof(struct hfi_msg_session_property_info_pkt),
+ .done = hfi_session_prop_info,
+ },
+ {.pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_release_resources_done_pkt),
+ .done = hfi_session_rel_res_done,
+ },
+ {.pkt = HFI_MSG_SESSION_GET_SEQUENCE_HEADER,
+ .pkt_sz = sizeof(struct hfi_msg_session_get_sequence_hdr_done_pkt),
+ .done = hfi_session_get_seq_hdr_done,
+ },
+ {.pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
+ .pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
+ .done = hfi_session_rel_buf_done,
+ },
+};
+
+void hfi_process_watchdog_timeout(struct venus_core *core)
+{
+ event_sys_error(core, EVT_SYS_WATCHDOG_TIMEOUT, NULL);
+}
+
+static struct venus_inst *to_instance(struct venus_core *core, u32 session_id)
+{
+ struct venus_inst *inst;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list)
+ if (hash32_ptr(inst) == session_id) {
+ mutex_unlock(&core->lock);
+ return inst;
+ }
+ mutex_unlock(&core->lock);
+
+ return NULL;
+}
+
+u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr)
+{
+ const struct hfi_done_handler *handler;
+ struct device *dev = core->dev;
+ struct venus_inst *inst;
+ bool found = false;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ handler = &handlers[i];
+ if (handler->pkt != hdr->pkt_type)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return hdr->pkt_type;
+
+ if (hdr->size && hdr->size < handler->pkt_sz &&
+ hdr->size < handler->pkt_sz2) {
+ dev_err(dev, "bad packet size (%d should be %d, pkt type:%x)\n",
+ hdr->size, handler->pkt_sz, hdr->pkt_type);
+
+ return hdr->pkt_type;
+ }
+
+ if (handler->is_sys_pkt) {
+ inst = NULL;
+ } else {
+ struct hfi_session_pkt *pkt;
+
+ pkt = (struct hfi_session_pkt *)hdr;
+ inst = to_instance(core, pkt->shdr.session_id);
+
+ if (!inst)
+ dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
+ pkt->shdr.session_id,
+ handler ? handler->pkt : 0);
+
+ /*
+ * Event of type HFI_EVENT_SYS_ERROR will not have any session
+ * associated with it
+ */
+ if (!inst && hdr->pkt_type != HFI_MSG_EVENT_NOTIFY) {
+ dev_err(dev, "got invalid session id:%x\n",
+ pkt->shdr.session_id);
+ goto invalid_session;
+ }
+ }
+
+ handler->done(core, inst, hdr);
+
+invalid_session:
+ return hdr->pkt_type;
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.h b/drivers/media/platform/qcom/venus/hfi_msgs.h
new file mode 100644
index 000000000000..14d9a3979b14
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HFI_MSGS_H__
+#define __VENUS_HFI_MSGS_H__
+
+/* message calls */
+#define HFI_MSG_SYS_INIT 0x20001
+#define HFI_MSG_SYS_PC_PREP 0x20002
+#define HFI_MSG_SYS_RELEASE_RESOURCE 0x20003
+#define HFI_MSG_SYS_DEBUG 0x20004
+#define HFI_MSG_SYS_SESSION_INIT 0x20006
+#define HFI_MSG_SYS_SESSION_END 0x20007
+#define HFI_MSG_SYS_IDLE 0x20008
+#define HFI_MSG_SYS_COV 0x20009
+#define HFI_MSG_SYS_PROPERTY_INFO 0x2000a
+
+#define HFI_MSG_EVENT_NOTIFY 0x21001
+#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER 0x21002
+
+#define HFI_MSG_SYS_PING_ACK 0x220002
+#define HFI_MSG_SYS_SESSION_ABORT 0x220004
+
+#define HFI_MSG_SESSION_LOAD_RESOURCES 0x221001
+#define HFI_MSG_SESSION_START 0x221002
+#define HFI_MSG_SESSION_STOP 0x221003
+#define HFI_MSG_SESSION_SUSPEND 0x221004
+#define HFI_MSG_SESSION_RESUME 0x221005
+#define HFI_MSG_SESSION_FLUSH 0x221006
+#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007
+#define HFI_MSG_SESSION_FILL_BUFFER 0x221008
+#define HFI_MSG_SESSION_PROPERTY_INFO 0x221009
+#define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a
+#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER 0x22100b
+#define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c
+
+#define HFI_PICTURE_I 0x00000001
+#define HFI_PICTURE_P 0x00000002
+#define HFI_PICTURE_B 0x00000004
+#define HFI_PICTURE_IDR 0x00000008
+#define HFI_FRAME_NOTCODED 0x7f002000
+#define HFI_FRAME_YUV 0x7f004000
+#define HFI_UNUSED_PICT 0x10000000
+
+/* message packets */
+struct hfi_msg_event_notify_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 event_id;
+ u32 event_data1;
+ u32 event_data2;
+ u32 ext_event_data[1];
+};
+
+struct hfi_msg_event_release_buffer_ref_pkt {
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 output_tag;
+};
+
+struct hfi_msg_sys_init_done_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 error_type;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_msg_sys_pc_prep_done_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 error_type;
+};
+
+struct hfi_msg_sys_release_resource_done_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 resource_handle;
+ u32 error_type;
+};
+
+struct hfi_msg_session_init_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_msg_session_end_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_get_sequence_hdr_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 header_len;
+ u32 sequence_header;
+};
+
+struct hfi_msg_sys_session_abort_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_sys_idle_pkt {
+ struct hfi_pkt_hdr hdr;
+};
+
+struct hfi_msg_sys_ping_ack_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 client_data;
+};
+
+struct hfi_msg_sys_property_info_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_msg_session_load_resources_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_start_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_stop_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_suspend_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_resume_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_flush_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 flush_type;
+};
+
+struct hfi_msg_session_empty_buffer_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 offset;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[0];
+};
+
+struct hfi_msg_session_fbd_compressed_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 error_type;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u32 output_tag;
+ u32 picture_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[0];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 view_id;
+ u32 error_type;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 frame_width;
+ u32 frame_height;
+ u32 start_x_coord;
+ u32 start_y_coord;
+ u32 input_tag;
+ u32 input_tag2;
+ u32 output_tag;
+ u32 picture_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[0];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane1_pkt {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 packet_buffer2;
+ u32 data[0];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane2_pkt {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 packet_buffer3;
+ u32 data[0];
+};
+
+struct hfi_msg_session_parse_sequence_header_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_msg_session_property_info_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[1];
+};
+
+struct hfi_msg_session_release_resources_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_release_buffers_done_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+ u32 num_buffers;
+ u32 buffer_info[1];
+};
+
+struct hfi_msg_sys_debug_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_type;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[1];
+};
+
+struct hfi_msg_sys_coverage_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[1];
+};
+
+struct venus_core;
+struct hfi_pkt_hdr;
+
+void hfi_process_watchdog_timeout(struct venus_core *core);
+u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
new file mode 100644
index 000000000000..1caae8feaa36
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -0,0 +1,1572 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/qcom_scm.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "hfi_cmds.h"
+#include "hfi_msgs.h"
+#include "hfi_venus.h"
+#include "hfi_venus_io.h"
+
+#define HFI_MASK_QHDR_TX_TYPE 0xff000000
+#define HFI_MASK_QHDR_RX_TYPE 0x00ff0000
+#define HFI_MASK_QHDR_PRI_TYPE 0x0000ff00
+#define HFI_MASK_QHDR_ID_TYPE 0x000000ff
+
+#define HFI_HOST_TO_CTRL_CMD_Q 0
+#define HFI_CTRL_TO_HOST_MSG_Q 1
+#define HFI_CTRL_TO_HOST_DBG_Q 2
+#define HFI_MASK_QHDR_STATUS 0x000000ff
+
+#define IFACEQ_NUM 3
+#define IFACEQ_CMD_IDX 0
+#define IFACEQ_MSG_IDX 1
+#define IFACEQ_DBG_IDX 2
+#define IFACEQ_MAX_BUF_COUNT 50
+#define IFACEQ_MAX_PARALLEL_CLNTS 16
+#define IFACEQ_DFLT_QHDR 0x01010000
+
+#define POLL_INTERVAL_US 50
+
+#define IFACEQ_MAX_PKT_SIZE 1024
+#define IFACEQ_MED_PKT_SIZE 768
+#define IFACEQ_MIN_PKT_SIZE 8
+#define IFACEQ_VAR_SMALL_PKT_SIZE 100
+#define IFACEQ_VAR_LARGE_PKT_SIZE 512
+#define IFACEQ_VAR_HUGE_PKT_SIZE (1024 * 12)
+
+enum tzbsp_video_state {
+ TZBSP_VIDEO_STATE_SUSPEND = 0,
+ TZBSP_VIDEO_STATE_RESUME
+};
+
+struct hfi_queue_table_header {
+ u32 version;
+ u32 size;
+ u32 qhdr0_offset;
+ u32 qhdr_size;
+ u32 num_q;
+ u32 num_active_q;
+};
+
+struct hfi_queue_header {
+ u32 status;
+ u32 start_addr;
+ u32 type;
+ u32 q_size;
+ u32 pkt_size;
+ u32 pkt_drop_cnt;
+ u32 rx_wm;
+ u32 tx_wm;
+ u32 rx_req;
+ u32 tx_req;
+ u32 rx_irq_status;
+ u32 tx_irq_status;
+ u32 read_idx;
+ u32 write_idx;
+};
+
+#define IFACEQ_TABLE_SIZE \
+ (sizeof(struct hfi_queue_table_header) + \
+ sizeof(struct hfi_queue_header) * IFACEQ_NUM)
+
+#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
+ IFACEQ_MAX_BUF_COUNT * IFACEQ_MAX_PARALLEL_CLNTS)
+
+#define IFACEQ_GET_QHDR_START_ADDR(ptr, i) \
+ (void *)(((ptr) + sizeof(struct hfi_queue_table_header)) + \
+ ((i) * sizeof(struct hfi_queue_header)))
+
+#define QDSS_SIZE SZ_4K
+#define SFR_SIZE SZ_4K
+#define QUEUE_SIZE \
+ (IFACEQ_TABLE_SIZE + (IFACEQ_QUEUE_SIZE * IFACEQ_NUM))
+
+#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K)
+#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K)
+#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K)
+#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \
+ ALIGNED_QDSS_SIZE, SZ_1M)
+
+struct mem_desc {
+ dma_addr_t da; /* device address */
+ void *kva; /* kernel virtual address */
+ u32 size;
+ unsigned long attrs;
+};
+
+struct iface_queue {
+ struct hfi_queue_header *qhdr;
+ struct mem_desc qmem;
+};
+
+enum venus_state {
+ VENUS_STATE_DEINIT = 1,
+ VENUS_STATE_INIT,
+};
+
+struct venus_hfi_device {
+ struct venus_core *core;
+ u32 irq_status;
+ u32 last_packet_type;
+ bool power_enabled;
+ bool suspended;
+ enum venus_state state;
+ /* serialize read / write to the shared memory */
+ struct mutex lock;
+ struct completion pwr_collapse_prep;
+ struct completion release_resource;
+ struct mem_desc ifaceq_table;
+ struct mem_desc sfr;
+ struct iface_queue queues[IFACEQ_NUM];
+ u8 pkt_buf[IFACEQ_VAR_HUGE_PKT_SIZE];
+ u8 dbg_buf[IFACEQ_VAR_HUGE_PKT_SIZE];
+};
+
+static bool venus_pkt_debug;
+static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL;
+static bool venus_sys_idle_indicator;
+static bool venus_fw_low_power_mode = true;
+static int venus_hw_rsp_timeout = 1000;
+static bool venus_fw_coverage;
+
+static void venus_set_state(struct venus_hfi_device *hdev,
+ enum venus_state state)
+{
+ mutex_lock(&hdev->lock);
+ hdev->state = state;
+ mutex_unlock(&hdev->lock);
+}
+
+static bool venus_is_valid_state(struct venus_hfi_device *hdev)
+{
+ return hdev->state != VENUS_STATE_DEINIT;
+}
+
+static void venus_dump_packet(struct venus_hfi_device *hdev, const void *packet)
+{
+ size_t pkt_size = *(u32 *)packet;
+
+ if (!venus_pkt_debug)
+ return;
+
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, packet,
+ pkt_size, true);
+}
+
+static int venus_write_queue(struct venus_hfi_device *hdev,
+ struct iface_queue *queue,
+ void *packet, u32 *rx_req)
+{
+ struct hfi_queue_header *qhdr;
+ u32 dwords, new_wr_idx;
+ u32 empty_space, rd_idx, wr_idx, qsize;
+ u32 *wr_ptr;
+
+ if (!queue->qmem.kva)
+ return -EINVAL;
+
+ qhdr = queue->qhdr;
+ if (!qhdr)
+ return -EINVAL;
+
+ venus_dump_packet(hdev, packet);
+
+ dwords = (*(u32 *)packet) >> 2;
+ if (!dwords)
+ return -EINVAL;
+
+ rd_idx = qhdr->read_idx;
+ wr_idx = qhdr->write_idx;
+ qsize = qhdr->q_size;
+ /* ensure rd/wr indices's are read from memory */
+ rmb();
+
+ if (wr_idx >= rd_idx)
+ empty_space = qsize - (wr_idx - rd_idx);
+ else
+ empty_space = rd_idx - wr_idx;
+
+ if (empty_space <= dwords) {
+ qhdr->tx_req = 1;
+ /* ensure tx_req is updated in memory */
+ wmb();
+ return -ENOSPC;
+ }
+
+ qhdr->tx_req = 0;
+ /* ensure tx_req is updated in memory */
+ wmb();
+
+ new_wr_idx = wr_idx + dwords;
+ wr_ptr = (u32 *)(queue->qmem.kva + (wr_idx << 2));
+ if (new_wr_idx < qsize) {
+ memcpy(wr_ptr, packet, dwords << 2);
+ } else {
+ size_t len;
+
+ new_wr_idx -= qsize;
+ len = (dwords - new_wr_idx) << 2;
+ memcpy(wr_ptr, packet, len);
+ memcpy(queue->qmem.kva, packet + len, new_wr_idx << 2);
+ }
+
+ /* make sure packet is written before updating the write index */
+ wmb();
+
+ qhdr->write_idx = new_wr_idx;
+ *rx_req = qhdr->rx_req ? 1 : 0;
+
+ /* make sure write index is updated before an interrupt is raised */
+ mb();
+
+ return 0;
+}
+
+static int venus_read_queue(struct venus_hfi_device *hdev,
+ struct iface_queue *queue, void *pkt, u32 *tx_req)
+{
+ struct hfi_queue_header *qhdr;
+ u32 dwords, new_rd_idx;
+ u32 rd_idx, wr_idx, type, qsize;
+ u32 *rd_ptr;
+ u32 recv_request = 0;
+ int ret = 0;
+
+ if (!queue->qmem.kva)
+ return -EINVAL;
+
+ qhdr = queue->qhdr;
+ if (!qhdr)
+ return -EINVAL;
+
+ type = qhdr->type;
+ rd_idx = qhdr->read_idx;
+ wr_idx = qhdr->write_idx;
+ qsize = qhdr->q_size;
+
+ /* make sure data is valid before using it */
+ rmb();
+
+ /*
+ * Do not set receive request for debug queue, if set, Venus generates
+ * interrupt for debug messages even when there is no response message
+ * available. In general debug queue will not become full as it is being
+ * emptied out for every interrupt from Venus. Venus will anyway
+ * generates interrupt if it is full.
+ */
+ if (type & HFI_CTRL_TO_HOST_MSG_Q)
+ recv_request = 1;
+
+ if (rd_idx == wr_idx) {
+ qhdr->rx_req = recv_request;
+ *tx_req = 0;
+ /* update rx_req field in memory */
+ wmb();
+ return -ENODATA;
+ }
+
+ rd_ptr = (u32 *)(queue->qmem.kva + (rd_idx << 2));
+ dwords = *rd_ptr >> 2;
+ if (!dwords)
+ return -EINVAL;
+
+ new_rd_idx = rd_idx + dwords;
+ if (((dwords << 2) <= IFACEQ_VAR_HUGE_PKT_SIZE) && rd_idx <= qsize) {
+ if (new_rd_idx < qsize) {
+ memcpy(pkt, rd_ptr, dwords << 2);
+ } else {
+ size_t len;
+
+ new_rd_idx -= qsize;
+ len = (dwords - new_rd_idx) << 2;
+ memcpy(pkt, rd_ptr, len);
+ memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2);
+ }
+ } else {
+ /* bad packet received, dropping */
+ new_rd_idx = qhdr->write_idx;
+ ret = -EBADMSG;
+ }
+
+ /* ensure the packet is read before updating read index */
+ rmb();
+
+ qhdr->read_idx = new_rd_idx;
+ /* ensure updating read index */
+ wmb();
+
+ rd_idx = qhdr->read_idx;
+ wr_idx = qhdr->write_idx;
+ /* ensure rd/wr indices are read from memory */
+ rmb();
+
+ if (rd_idx != wr_idx)
+ qhdr->rx_req = 0;
+ else
+ qhdr->rx_req = recv_request;
+
+ *tx_req = qhdr->tx_req ? 1 : 0;
+
+ /* ensure rx_req is stored to memory and tx_req is loaded from memory */
+ mb();
+
+ venus_dump_packet(hdev, pkt);
+
+ return ret;
+}
+
+static int venus_alloc(struct venus_hfi_device *hdev, struct mem_desc *desc,
+ u32 size)
+{
+ struct device *dev = hdev->core->dev;
+
+ desc->attrs = DMA_ATTR_WRITE_COMBINE;
+ desc->size = ALIGN(size, SZ_4K);
+
+ desc->kva = dma_alloc_attrs(dev, size, &desc->da, GFP_KERNEL,
+ desc->attrs);
+ if (!desc->kva)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem)
+{
+ struct device *dev = hdev->core->dev;
+
+ dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs);
+}
+
+static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value)
+{
+ writel(value, hdev->core->base + reg);
+}
+
+static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg)
+{
+ return readl(hdev->core->base + reg);
+}
+
+static void venus_set_registers(struct venus_hfi_device *hdev)
+{
+ const struct venus_resources *res = hdev->core->res;
+ const struct reg_val *tbl = res->reg_tbl;
+ unsigned int count = res->reg_tbl_size;
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ venus_writel(hdev, tbl[i].reg, tbl[i].value);
+}
+
+static void venus_soft_int(struct venus_hfi_device *hdev)
+{
+ venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT));
+}
+
+static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev,
+ void *pkt)
+{
+ struct device *dev = hdev->core->dev;
+ struct hfi_pkt_hdr *cmd_packet;
+ struct iface_queue *queue;
+ u32 rx_req;
+ int ret;
+
+ if (!venus_is_valid_state(hdev))
+ return -EINVAL;
+
+ cmd_packet = (struct hfi_pkt_hdr *)pkt;
+ hdev->last_packet_type = cmd_packet->pkt_type;
+
+ queue = &hdev->queues[IFACEQ_CMD_IDX];
+
+ ret = venus_write_queue(hdev, queue, pkt, &rx_req);
+ if (ret) {
+ dev_err(dev, "write to iface cmd queue failed (%d)\n", ret);
+ return ret;
+ }
+
+ if (rx_req)
+ venus_soft_int(hdev);
+
+ return 0;
+}
+
+static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt)
+{
+ int ret;
+
+ mutex_lock(&hdev->lock);
+ ret = venus_iface_cmdq_write_nolock(hdev, pkt);
+ mutex_unlock(&hdev->lock);
+
+ return ret;
+}
+
+static int venus_hfi_core_set_resource(struct venus_core *core, u32 id,
+ u32 size, u32 addr, void *cookie)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct hfi_sys_set_resource_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ if (id == VIDC_RESOURCE_NONE)
+ return 0;
+
+ pkt = (struct hfi_sys_set_resource_pkt *)packet;
+
+ ret = pkt_sys_set_resource(pkt, id, size, addr, cookie);
+ if (ret)
+ return ret;
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_boot_core(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ static const unsigned int max_tries = 100;
+ u32 ctrl_status = 0;
+ unsigned int count = 0;
+ int ret = 0;
+
+ venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT));
+ venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK);
+ venus_writel(hdev, CPU_CS_SCIACMDARG3, 1);
+
+ while (!ctrl_status && count < max_tries) {
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) {
+ dev_err(dev, "invalid setting for UC_REGION\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ usleep_range(500, 1000);
+ count++;
+ }
+
+ if (count >= max_tries)
+ ret = -ETIMEDOUT;
+
+ return ret;
+}
+
+static u32 venus_hwversion(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION);
+ u32 major, minor, step;
+
+ major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK;
+ major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT;
+ minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK;
+ minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT;
+ step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK;
+
+ dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step);
+
+ return major;
+}
+
+static int venus_run(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ int ret;
+
+ /*
+ * Re-program all of the registers that get reset as a result of
+ * regulator_disable() and _enable()
+ */
+ venus_set_registers(hdev);
+
+ venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da);
+ venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE);
+ venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da);
+ venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01);
+ if (hdev->sfr.da)
+ venus_writel(hdev, SFR_ADDR, hdev->sfr.da);
+
+ ret = venus_boot_core(hdev);
+ if (ret) {
+ dev_err(dev, "failed to reset venus core\n");
+ return ret;
+ }
+
+ venus_hwversion(hdev);
+
+ return 0;
+}
+
+static int venus_halt_axi(struct venus_hfi_device *hdev)
+{
+ void __iomem *base = hdev->core->base;
+ struct device *dev = hdev->core->dev;
+ u32 val;
+ int ret;
+
+ /* Halt AXI and AXI IMEM VBIF Access */
+ val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0);
+ val |= VBIF_AXI_HALT_CTRL0_HALT_REQ;
+ venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val);
+
+ /* Request for AXI bus port halt */
+ ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val,
+ val & VBIF_AXI_HALT_CTRL1_HALT_ACK,
+ POLL_INTERVAL_US,
+ VBIF_AXI_HALT_ACK_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "AXI bus port halt timeout\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int venus_power_off(struct venus_hfi_device *hdev)
+{
+ int ret;
+
+ if (!hdev->power_enabled)
+ return 0;
+
+ ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0);
+ if (ret)
+ return ret;
+
+ ret = venus_halt_axi(hdev);
+ if (ret)
+ return ret;
+
+ hdev->power_enabled = false;
+
+ return 0;
+}
+
+static int venus_power_on(struct venus_hfi_device *hdev)
+{
+ int ret;
+
+ if (hdev->power_enabled)
+ return 0;
+
+ ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_RESUME, 0);
+ if (ret)
+ goto err;
+
+ ret = venus_run(hdev);
+ if (ret)
+ goto err_suspend;
+
+ hdev->power_enabled = true;
+
+ return 0;
+
+err_suspend:
+ qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0);
+err:
+ hdev->power_enabled = false;
+ return ret;
+}
+
+static int venus_iface_msgq_read_nolock(struct venus_hfi_device *hdev,
+ void *pkt)
+{
+ struct iface_queue *queue;
+ u32 tx_req;
+ int ret;
+
+ if (!venus_is_valid_state(hdev))
+ return -EINVAL;
+
+ queue = &hdev->queues[IFACEQ_MSG_IDX];
+
+ ret = venus_read_queue(hdev, queue, pkt, &tx_req);
+ if (ret)
+ return ret;
+
+ if (tx_req)
+ venus_soft_int(hdev);
+
+ return 0;
+}
+
+static int venus_iface_msgq_read(struct venus_hfi_device *hdev, void *pkt)
+{
+ int ret;
+
+ mutex_lock(&hdev->lock);
+ ret = venus_iface_msgq_read_nolock(hdev, pkt);
+ mutex_unlock(&hdev->lock);
+
+ return ret;
+}
+
+static int venus_iface_dbgq_read_nolock(struct venus_hfi_device *hdev,
+ void *pkt)
+{
+ struct iface_queue *queue;
+ u32 tx_req;
+ int ret;
+
+ ret = venus_is_valid_state(hdev);
+ if (!ret)
+ return -EINVAL;
+
+ queue = &hdev->queues[IFACEQ_DBG_IDX];
+
+ ret = venus_read_queue(hdev, queue, pkt, &tx_req);
+ if (ret)
+ return ret;
+
+ if (tx_req)
+ venus_soft_int(hdev);
+
+ return 0;
+}
+
+static int venus_iface_dbgq_read(struct venus_hfi_device *hdev, void *pkt)
+{
+ int ret;
+
+ if (!pkt)
+ return -EINVAL;
+
+ mutex_lock(&hdev->lock);
+ ret = venus_iface_dbgq_read_nolock(hdev, pkt);
+ mutex_unlock(&hdev->lock);
+
+ return ret;
+}
+
+static void venus_set_qhdr_defaults(struct hfi_queue_header *qhdr)
+{
+ qhdr->status = 1;
+ qhdr->type = IFACEQ_DFLT_QHDR;
+ qhdr->q_size = IFACEQ_QUEUE_SIZE / 4;
+ qhdr->pkt_size = 0;
+ qhdr->rx_wm = 1;
+ qhdr->tx_wm = 1;
+ qhdr->rx_req = 1;
+ qhdr->tx_req = 0;
+ qhdr->rx_irq_status = 0;
+ qhdr->tx_irq_status = 0;
+ qhdr->read_idx = 0;
+ qhdr->write_idx = 0;
+}
+
+static void venus_interface_queues_release(struct venus_hfi_device *hdev)
+{
+ mutex_lock(&hdev->lock);
+
+ venus_free(hdev, &hdev->ifaceq_table);
+ venus_free(hdev, &hdev->sfr);
+
+ memset(hdev->queues, 0, sizeof(hdev->queues));
+ memset(&hdev->ifaceq_table, 0, sizeof(hdev->ifaceq_table));
+ memset(&hdev->sfr, 0, sizeof(hdev->sfr));
+
+ mutex_unlock(&hdev->lock);
+}
+
+static int venus_interface_queues_init(struct venus_hfi_device *hdev)
+{
+ struct hfi_queue_table_header *tbl_hdr;
+ struct iface_queue *queue;
+ struct hfi_sfr *sfr;
+ struct mem_desc desc = {0};
+ unsigned int offset;
+ unsigned int i;
+ int ret;
+
+ ret = venus_alloc(hdev, &desc, ALIGNED_QUEUE_SIZE);
+ if (ret)
+ return ret;
+
+ hdev->ifaceq_table.kva = desc.kva;
+ hdev->ifaceq_table.da = desc.da;
+ hdev->ifaceq_table.size = IFACEQ_TABLE_SIZE;
+ offset = hdev->ifaceq_table.size;
+
+ for (i = 0; i < IFACEQ_NUM; i++) {
+ queue = &hdev->queues[i];
+ queue->qmem.da = desc.da + offset;
+ queue->qmem.kva = desc.kva + offset;
+ queue->qmem.size = IFACEQ_QUEUE_SIZE;
+ offset += queue->qmem.size;
+ queue->qhdr =
+ IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i);
+
+ venus_set_qhdr_defaults(queue->qhdr);
+
+ queue->qhdr->start_addr = queue->qmem.da;
+
+ if (i == IFACEQ_CMD_IDX)
+ queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q;
+ else if (i == IFACEQ_MSG_IDX)
+ queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q;
+ else if (i == IFACEQ_DBG_IDX)
+ queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q;
+ }
+
+ tbl_hdr = hdev->ifaceq_table.kva;
+ tbl_hdr->version = 0;
+ tbl_hdr->size = IFACEQ_TABLE_SIZE;
+ tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header);
+ tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header);
+ tbl_hdr->num_q = IFACEQ_NUM;
+ tbl_hdr->num_active_q = IFACEQ_NUM;
+
+ /*
+ * Set receive request to zero on debug queue as there is no
+ * need of interrupt from video hardware for debug messages
+ */
+ queue = &hdev->queues[IFACEQ_DBG_IDX];
+ queue->qhdr->rx_req = 0;
+
+ ret = venus_alloc(hdev, &desc, ALIGNED_SFR_SIZE);
+ if (ret) {
+ hdev->sfr.da = 0;
+ } else {
+ hdev->sfr.da = desc.da;
+ hdev->sfr.kva = desc.kva;
+ hdev->sfr.size = ALIGNED_SFR_SIZE;
+ sfr = hdev->sfr.kva;
+ sfr->buf_size = ALIGNED_SFR_SIZE;
+ }
+
+ /* ensure table and queue header structs are settled in memory */
+ wmb();
+
+ return 0;
+}
+
+static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_sys_set_property_pkt *)packet;
+
+ pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug);
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_sys_set_property_pkt *)packet;
+
+ pkt_sys_coverage_config(pkt, mode);
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_sys_set_idle_message(struct venus_hfi_device *hdev,
+ bool enable)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ pkt = (struct hfi_sys_set_property_pkt *)packet;
+
+ pkt_sys_idle_indicator(pkt, enable);
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_sys_set_power_control(struct venus_hfi_device *hdev,
+ bool enable)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_sys_set_property_pkt *)packet;
+
+ pkt_sys_power_control(pkt, enable);
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_get_queue_size(struct venus_hfi_device *hdev,
+ unsigned int index)
+{
+ struct hfi_queue_header *qhdr;
+
+ if (index >= IFACEQ_NUM)
+ return -EINVAL;
+
+ qhdr = hdev->queues[index].qhdr;
+ if (!qhdr)
+ return -EINVAL;
+
+ return abs(qhdr->read_idx - qhdr->write_idx);
+}
+
+static int venus_sys_set_default_properties(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ int ret;
+
+ ret = venus_sys_set_debug(hdev, venus_fw_debug);
+ if (ret)
+ dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret);
+
+ ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator);
+ if (ret)
+ dev_warn(dev, "setting idle response ON failed (%d)\n", ret);
+
+ ret = venus_sys_set_power_control(hdev, venus_fw_low_power_mode);
+ if (ret)
+ dev_warn(dev, "setting hw power collapse ON failed (%d)\n",
+ ret);
+
+ return ret;
+}
+
+static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_pkt pkt;
+
+ pkt_session_cmd(&pkt, pkt_type, inst);
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static void venus_flush_debug_queue(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ void *packet = hdev->dbg_buf;
+
+ while (!venus_iface_dbgq_read(hdev, packet)) {
+ struct hfi_msg_sys_coverage_pkt *pkt = packet;
+
+ if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
+ struct hfi_msg_sys_debug_pkt *pkt = packet;
+
+ dev_dbg(dev, "%s", pkt->msg_data);
+ }
+ }
+}
+
+static int venus_prepare_power_collapse(struct venus_hfi_device *hdev,
+ bool wait)
+{
+ unsigned long timeout = msecs_to_jiffies(venus_hw_rsp_timeout);
+ struct hfi_sys_pc_prep_pkt pkt;
+ int ret;
+
+ init_completion(&hdev->pwr_collapse_prep);
+
+ pkt_sys_pc_prep(&pkt);
+
+ ret = venus_iface_cmdq_write(hdev, &pkt);
+ if (ret)
+ return ret;
+
+ if (!wait)
+ return 0;
+
+ ret = wait_for_completion_timeout(&hdev->pwr_collapse_prep, timeout);
+ if (!ret) {
+ venus_flush_debug_queue(hdev);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int venus_are_queues_empty(struct venus_hfi_device *hdev)
+{
+ int ret1, ret2;
+
+ ret1 = venus_get_queue_size(hdev, IFACEQ_MSG_IDX);
+ if (ret1 < 0)
+ return ret1;
+
+ ret2 = venus_get_queue_size(hdev, IFACEQ_CMD_IDX);
+ if (ret2 < 0)
+ return ret2;
+
+ if (!ret1 && !ret2)
+ return 1;
+
+ return 0;
+}
+
+static void venus_sfr_print(struct venus_hfi_device *hdev)
+{
+ struct device *dev = hdev->core->dev;
+ struct hfi_sfr *sfr = hdev->sfr.kva;
+ void *p;
+
+ if (!sfr)
+ return;
+
+ p = memchr(sfr->data, '\0', sfr->buf_size);
+ /*
+ * SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates
+ * that Venus is in the process of crashing.
+ */
+ if (!p)
+ sfr->data[sfr->buf_size - 1] = '\0';
+
+ dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data);
+}
+
+static void venus_process_msg_sys_error(struct venus_hfi_device *hdev,
+ void *packet)
+{
+ struct hfi_msg_event_notify_pkt *event_pkt = packet;
+
+ if (event_pkt->event_id != HFI_EVENT_SYS_ERROR)
+ return;
+
+ venus_set_state(hdev, VENUS_STATE_DEINIT);
+
+ /*
+ * Once SYS_ERROR received from HW, it is safe to halt the AXI.
+ * With SYS_ERROR, Venus FW may have crashed and HW might be
+ * active and causing unnecessary transactions. Hence it is
+ * safe to stop all AXI transactions from venus subsystem.
+ */
+ venus_halt_axi(hdev);
+ venus_sfr_print(hdev);
+}
+
+static irqreturn_t venus_isr_thread(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ const struct venus_resources *res;
+ void *pkt;
+ u32 msg_ret;
+
+ if (!hdev)
+ return IRQ_NONE;
+
+ res = hdev->core->res;
+ pkt = hdev->pkt_buf;
+
+ if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) {
+ venus_sfr_print(hdev);
+ hfi_process_watchdog_timeout(core);
+ }
+
+ while (!venus_iface_msgq_read(hdev, pkt)) {
+ msg_ret = hfi_process_msg_packet(core, pkt);
+ switch (msg_ret) {
+ case HFI_MSG_EVENT_NOTIFY:
+ venus_process_msg_sys_error(hdev, pkt);
+ break;
+ case HFI_MSG_SYS_INIT:
+ venus_hfi_core_set_resource(core, res->vmem_id,
+ res->vmem_size,
+ res->vmem_addr,
+ hdev);
+ break;
+ case HFI_MSG_SYS_RELEASE_RESOURCE:
+ complete(&hdev->release_resource);
+ break;
+ case HFI_MSG_SYS_PC_PREP:
+ complete(&hdev->pwr_collapse_prep);
+ break;
+ default:
+ break;
+ }
+ }
+
+ venus_flush_debug_queue(hdev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t venus_isr(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ u32 status;
+
+ if (!hdev)
+ return IRQ_NONE;
+
+ status = venus_readl(hdev, WRAPPER_INTR_STATUS);
+
+ if (status & WRAPPER_INTR_STATUS_A2H_MASK ||
+ status & WRAPPER_INTR_STATUS_A2HWD_MASK ||
+ status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
+ hdev->irq_status = status;
+
+ venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1);
+ venus_writel(hdev, WRAPPER_INTR_CLEAR, status);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int venus_core_init(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct device *dev = core->dev;
+ struct hfi_sys_get_property_pkt version_pkt;
+ struct hfi_sys_init_pkt pkt;
+ int ret;
+
+ pkt_sys_init(&pkt, HFI_VIDEO_ARCH_OX);
+
+ venus_set_state(hdev, VENUS_STATE_INIT);
+
+ ret = venus_iface_cmdq_write(hdev, &pkt);
+ if (ret)
+ return ret;
+
+ pkt_sys_image_version(&version_pkt);
+
+ ret = venus_iface_cmdq_write(hdev, &version_pkt);
+ if (ret)
+ dev_warn(dev, "failed to send image version pkt to fw\n");
+
+ return 0;
+}
+
+static int venus_core_deinit(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+
+ venus_set_state(hdev, VENUS_STATE_DEINIT);
+ hdev->suspended = true;
+ hdev->power_enabled = false;
+
+ return 0;
+}
+
+static int venus_core_ping(struct venus_core *core, u32 cookie)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct hfi_sys_ping_pkt pkt;
+
+ pkt_sys_ping(&pkt, cookie);
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct hfi_sys_test_ssr_pkt pkt;
+ int ret;
+
+ ret = pkt_sys_ssr_cmd(&pkt, trigger_type);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static int venus_session_init(struct venus_inst *inst, u32 session_type,
+ u32 codec)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_init_pkt pkt;
+ int ret;
+
+ ret = venus_sys_set_default_properties(hdev);
+ if (ret)
+ return ret;
+
+ ret = pkt_session_init(&pkt, inst, session_type, codec);
+ if (ret)
+ goto err;
+
+ ret = venus_iface_cmdq_write(hdev, &pkt);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ venus_flush_debug_queue(hdev);
+ return ret;
+}
+
+static int venus_session_end(struct venus_inst *inst)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct device *dev = hdev->core->dev;
+
+ if (venus_fw_coverage) {
+ if (venus_sys_set_coverage(hdev, venus_fw_coverage))
+ dev_warn(dev, "fw coverage msg ON failed\n");
+ }
+
+ return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END);
+}
+
+static int venus_session_abort(struct venus_inst *inst)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+
+ venus_flush_debug_queue(hdev);
+
+ return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT);
+}
+
+static int venus_session_flush(struct venus_inst *inst, u32 flush_mode)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_flush_pkt pkt;
+ int ret;
+
+ ret = pkt_session_flush(&pkt, inst, flush_mode);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static int venus_session_start(struct venus_inst *inst)
+{
+ return venus_session_cmd(inst, HFI_CMD_SESSION_START);
+}
+
+static int venus_session_stop(struct venus_inst *inst)
+{
+ return venus_session_cmd(inst, HFI_CMD_SESSION_STOP);
+}
+
+static int venus_session_continue(struct venus_inst *inst)
+{
+ return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE);
+}
+
+static int venus_session_etb(struct venus_inst *inst,
+ struct hfi_frame_data *in_frame)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ u32 session_type = inst->session_type;
+ int ret;
+
+ if (session_type == VIDC_SESSION_TYPE_DEC) {
+ struct hfi_session_empty_buffer_compressed_pkt pkt;
+
+ ret = pkt_session_etb_decoder(&pkt, inst, in_frame);
+ if (ret)
+ return ret;
+
+ ret = venus_iface_cmdq_write(hdev, &pkt);
+ } else if (session_type == VIDC_SESSION_TYPE_ENC) {
+ struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt;
+
+ ret = pkt_session_etb_encoder(&pkt, inst, in_frame);
+ if (ret)
+ return ret;
+
+ ret = venus_iface_cmdq_write(hdev, &pkt);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int venus_session_ftb(struct venus_inst *inst,
+ struct hfi_frame_data *out_frame)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_fill_buffer_pkt pkt;
+ int ret;
+
+ ret = pkt_session_ftb(&pkt, inst, out_frame);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static int venus_session_set_buffers(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_set_buffers_pkt *pkt;
+ u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
+ int ret;
+
+ if (bd->buffer_type == HFI_BUFFER_INPUT)
+ return 0;
+
+ pkt = (struct hfi_session_set_buffers_pkt *)packet;
+
+ ret = pkt_session_set_buffers(pkt, inst, bd);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, pkt);
+}
+
+static int venus_session_unset_buffers(struct venus_inst *inst,
+ struct hfi_buffer_desc *bd)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_release_buffer_pkt *pkt;
+ u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
+ int ret;
+
+ if (bd->buffer_type == HFI_BUFFER_INPUT)
+ return 0;
+
+ pkt = (struct hfi_session_release_buffer_pkt *)packet;
+
+ ret = pkt_session_unset_buffers(pkt, inst, bd);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, pkt);
+}
+
+static int venus_session_load_res(struct venus_inst *inst)
+{
+ return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES);
+}
+
+static int venus_session_release_res(struct venus_inst *inst)
+{
+ return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES);
+}
+
+static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
+ u32 seq_hdr_len)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_parse_sequence_header_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_session_parse_sequence_header_pkt *)packet;
+
+ ret = pkt_session_parse_seq_header(pkt, inst, seq_hdr, seq_hdr_len);
+ if (ret)
+ return ret;
+
+ ret = venus_iface_cmdq_write(hdev, pkt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
+ u32 seq_hdr_len)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_get_sequence_header_pkt *pkt;
+ u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_session_get_sequence_header_pkt *)packet;
+
+ ret = pkt_session_get_seq_hdr(pkt, inst, seq_hdr, seq_hdr_len);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, pkt);
+}
+
+static int venus_session_set_property(struct venus_inst *inst, u32 ptype,
+ void *pdata)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_set_property_pkt *pkt;
+ u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
+ int ret;
+
+ pkt = (struct hfi_session_set_property_pkt *)packet;
+
+ ret = pkt_session_set_property(pkt, inst, ptype, pdata);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, pkt);
+}
+
+static int venus_session_get_property(struct venus_inst *inst, u32 ptype)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
+ struct hfi_session_get_property_pkt pkt;
+ int ret;
+
+ ret = pkt_session_get_property(&pkt, inst, ptype);
+ if (ret)
+ return ret;
+
+ return venus_iface_cmdq_write(hdev, &pkt);
+}
+
+static int venus_resume(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ int ret = 0;
+
+ mutex_lock(&hdev->lock);
+
+ if (!hdev->suspended)
+ goto unlock;
+
+ ret = venus_power_on(hdev);
+
+unlock:
+ if (!ret)
+ hdev->suspended = false;
+
+ mutex_unlock(&hdev->lock);
+
+ return ret;
+}
+
+static int venus_suspend_1xx(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct device *dev = core->dev;
+ u32 ctrl_status;
+ int ret;
+
+ if (!hdev->power_enabled || hdev->suspended)
+ return 0;
+
+ mutex_lock(&hdev->lock);
+ ret = venus_is_valid_state(hdev);
+ mutex_unlock(&hdev->lock);
+
+ if (!ret) {
+ dev_err(dev, "bad state, cannot suspend\n");
+ return -EINVAL;
+ }
+
+ ret = venus_prepare_power_collapse(hdev, true);
+ if (ret) {
+ dev_err(dev, "prepare for power collapse fail (%d)\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&hdev->lock);
+
+ if (hdev->last_packet_type != HFI_CMD_SYS_PC_PREP) {
+ mutex_unlock(&hdev->lock);
+ return -EINVAL;
+ }
+
+ ret = venus_are_queues_empty(hdev);
+ if (ret < 0 || !ret) {
+ mutex_unlock(&hdev->lock);
+ return -EINVAL;
+ }
+
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) {
+ mutex_unlock(&hdev->lock);
+ return -EINVAL;
+ }
+
+ ret = venus_power_off(hdev);
+ if (ret) {
+ mutex_unlock(&hdev->lock);
+ return ret;
+ }
+
+ hdev->suspended = true;
+
+ mutex_unlock(&hdev->lock);
+
+ return 0;
+}
+
+static int venus_suspend_3xx(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+ struct device *dev = core->dev;
+ u32 ctrl_status, wfi_status;
+ int ret;
+ int cnt = 100;
+
+ if (!hdev->power_enabled || hdev->suspended)
+ return 0;
+
+ mutex_lock(&hdev->lock);
+ ret = venus_is_valid_state(hdev);
+ mutex_unlock(&hdev->lock);
+
+ if (!ret) {
+ dev_err(dev, "bad state, cannot suspend\n");
+ return -EINVAL;
+ }
+
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) {
+ wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+
+ ret = venus_prepare_power_collapse(hdev, false);
+ if (ret) {
+ dev_err(dev, "prepare for power collapse fail (%d)\n",
+ ret);
+ return ret;
+ }
+
+ cnt = 100;
+ while (cnt--) {
+ wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY &&
+ wfi_status & BIT(0))
+ break;
+ usleep_range(1000, 1500);
+ }
+ }
+
+ mutex_lock(&hdev->lock);
+
+ ret = venus_power_off(hdev);
+ if (ret) {
+ dev_err(dev, "venus_power_off (%d)\n", ret);
+ mutex_unlock(&hdev->lock);
+ return ret;
+ }
+
+ hdev->suspended = true;
+
+ mutex_unlock(&hdev->lock);
+
+ return 0;
+}
+
+static int venus_suspend(struct venus_core *core)
+{
+ if (core->res->hfi_version == HFI_VERSION_3XX)
+ return venus_suspend_3xx(core);
+
+ return venus_suspend_1xx(core);
+}
+
+static const struct hfi_ops venus_hfi_ops = {
+ .core_init = venus_core_init,
+ .core_deinit = venus_core_deinit,
+ .core_ping = venus_core_ping,
+ .core_trigger_ssr = venus_core_trigger_ssr,
+
+ .session_init = venus_session_init,
+ .session_end = venus_session_end,
+ .session_abort = venus_session_abort,
+ .session_flush = venus_session_flush,
+ .session_start = venus_session_start,
+ .session_stop = venus_session_stop,
+ .session_continue = venus_session_continue,
+ .session_etb = venus_session_etb,
+ .session_ftb = venus_session_ftb,
+ .session_set_buffers = venus_session_set_buffers,
+ .session_unset_buffers = venus_session_unset_buffers,
+ .session_load_res = venus_session_load_res,
+ .session_release_res = venus_session_release_res,
+ .session_parse_seq_hdr = venus_session_parse_seq_hdr,
+ .session_get_seq_hdr = venus_session_get_seq_hdr,
+ .session_set_property = venus_session_set_property,
+ .session_get_property = venus_session_get_property,
+
+ .resume = venus_resume,
+ .suspend = venus_suspend,
+
+ .isr = venus_isr,
+ .isr_thread = venus_isr_thread,
+};
+
+void venus_hfi_destroy(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev = to_hfi_priv(core);
+
+ venus_interface_queues_release(hdev);
+ mutex_destroy(&hdev->lock);
+ kfree(hdev);
+ core->priv = NULL;
+ core->ops = NULL;
+}
+
+int venus_hfi_create(struct venus_core *core)
+{
+ struct venus_hfi_device *hdev;
+ int ret;
+
+ hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
+ if (!hdev)
+ return -ENOMEM;
+
+ mutex_init(&hdev->lock);
+
+ hdev->core = core;
+ hdev->suspended = true;
+ core->priv = hdev;
+ core->ops = &venus_hfi_ops;
+ core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY |
+ ENC_DEINTERLACE_CAPABILITY |
+ DEC_MULTI_STREAM_CAPABILITY;
+
+ ret = venus_interface_queues_init(hdev);
+ if (ret)
+ goto err_kfree;
+
+ return 0;
+
+err_kfree:
+ kfree(hdev);
+ core->priv = NULL;
+ core->ops = NULL;
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.h b/drivers/media/platform/qcom/venus/hfi_venus.h
new file mode 100644
index 000000000000..885923354033
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_venus.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HFI_VENUS_H__
+#define __VENUS_HFI_VENUS_H__
+
+struct venus_core;
+
+void venus_hfi_destroy(struct venus_core *core);
+int venus_hfi_create(struct venus_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h
new file mode 100644
index 000000000000..98cc350113ab
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_HFI_VENUS_IO_H__
+#define __VENUS_HFI_VENUS_IO_H__
+
+#define VBIF_BASE 0x80000
+
+#define VBIF_AXI_HALT_CTRL0 (VBIF_BASE + 0x208)
+#define VBIF_AXI_HALT_CTRL1 (VBIF_BASE + 0x20c)
+
+#define VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0)
+#define VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0)
+#define VBIF_AXI_HALT_ACK_TIMEOUT_US 500000
+
+#define CPU_BASE 0xc0000
+#define CPU_CS_BASE (CPU_BASE + 0x12000)
+#define CPU_IC_BASE (CPU_BASE + 0x1f000)
+
+#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE + 0x1c)
+
+#define VIDC_CTRL_INIT (CPU_CS_BASE + 0x48)
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1_MASK 0xfffffffe
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1_SHIFT 1
+#define VIDC_CTRL_INIT_CTRL_MASK 0x1
+#define VIDC_CTRL_INIT_CTRL_SHIFT 0
+
+/* HFI control status */
+#define CPU_CS_SCIACMDARG0 (CPU_CS_BASE + 0x4c)
+#define CPU_CS_SCIACMDARG0_MASK 0xff
+#define CPU_CS_SCIACMDARG0_SHIFT 0x0
+#define CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK 0xfe
+#define CPU_CS_SCIACMDARG0_ERROR_STATUS_SHIFT 0x1
+#define CPU_CS_SCIACMDARG0_INIT_STATUS_MASK 0x1
+#define CPU_CS_SCIACMDARG0_INIT_STATUS_SHIFT 0x0
+#define CPU_CS_SCIACMDARG0_PC_READY BIT(8)
+#define CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK BIT(30)
+
+/* HFI queue table info */
+#define CPU_CS_SCIACMDARG1 (CPU_CS_BASE + 0x50)
+
+/* HFI queue table address */
+#define CPU_CS_SCIACMDARG2 (CPU_CS_BASE + 0x54)
+
+/* Venus cpu */
+#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE + 0x58)
+
+#define SFR_ADDR (CPU_CS_BASE + 0x5c)
+#define MMAP_ADDR (CPU_CS_BASE + 0x60)
+#define UC_REGION_ADDR (CPU_CS_BASE + 0x64)
+#define UC_REGION_SIZE (CPU_CS_BASE + 0x68)
+
+#define CPU_IC_SOFTINT (CPU_IC_BASE + 0x18)
+#define CPU_IC_SOFTINT_H2A_MASK 0x8000
+#define CPU_IC_SOFTINT_H2A_SHIFT 0xf
+
+/* Venus wrapper */
+#define WRAPPER_BASE 0x000e0000
+
+#define WRAPPER_HW_VERSION (WRAPPER_BASE + 0x00)
+#define WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000
+#define WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28
+#define WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xfff0000
+#define WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16
+#define WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xffff
+
+#define WRAPPER_CLOCK_CONFIG (WRAPPER_BASE + 0x04)
+
+#define WRAPPER_INTR_STATUS (WRAPPER_BASE + 0x0c)
+#define WRAPPER_INTR_STATUS_A2HWD_MASK 0x10
+#define WRAPPER_INTR_STATUS_A2HWD_SHIFT 0x4
+#define WRAPPER_INTR_STATUS_A2H_MASK 0x4
+#define WRAPPER_INTR_STATUS_A2H_SHIFT 0x2
+
+#define WRAPPER_INTR_MASK (WRAPPER_BASE + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BASK 0x10
+#define WRAPPER_INTR_MASK_A2HWD_SHIFT 0x4
+#define WRAPPER_INTR_MASK_A2HVCODEC_MASK 0x8
+#define WRAPPER_INTR_MASK_A2HVCODEC_SHIFT 0x3
+#define WRAPPER_INTR_MASK_A2HCPU_MASK 0x4
+#define WRAPPER_INTR_MASK_A2HCPU_SHIFT 0x2
+
+#define WRAPPER_INTR_CLEAR (WRAPPER_BASE + 0x14)
+#define WRAPPER_INTR_CLEAR_A2HWD_MASK 0x10
+#define WRAPPER_INTR_CLEAR_A2HWD_SHIFT 0x4
+#define WRAPPER_INTR_CLEAR_A2H_MASK 0x4
+#define WRAPPER_INTR_CLEAR_A2H_SHIFT 0x2
+
+#define WRAPPER_POWER_STATUS (WRAPPER_BASE + 0x44)
+#define WRAPPER_VDEC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x48)
+#define WRAPPER_VENC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x4c)
+#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET (WRAPPER_BASE + 0x64)
+
+#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000)
+#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008)
+#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c)
+
+#define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010)
+#define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014)
+#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000)
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
new file mode 100644
index 000000000000..eb0c1c51cfef
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "hfi_venus_io.h"
+#include "core.h"
+#include "helpers.h"
+#include "vdec.h"
+
+static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height)
+{
+ u32 y_stride, uv_stride, y_plane;
+ u32 y_sclines, uv_sclines, uv_plane;
+ u32 size;
+
+ y_stride = ALIGN(width, 128);
+ uv_stride = ALIGN(width, 128);
+ y_sclines = ALIGN(height, 32);
+ uv_sclines = ALIGN(((height + 1) >> 1), 16);
+
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines + SZ_4K;
+ size = y_plane + uv_plane + SZ_8K;
+
+ return ALIGN(size, SZ_4K);
+}
+
+static u32 get_framesize_compressed(unsigned int width, unsigned int height)
+{
+ return ((width * height * 3 / 2) / 2) + 128;
+}
+
+/*
+ * Three resons to keep MPLANE formats (despite that the number of planes
+ * currently is one):
+ * - the MPLANE formats allow only one plane to be used
+ * - the downstream driver use MPLANE formats too
+ * - future firmware versions could add support for >1 planes
+ */
+static const struct venus_format vdec_formats[] = {
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_MPEG2,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VP8,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_XVID,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
+static const struct venus_format *find_format(u32 pixfmt, u32 type)
+{
+ const struct venus_format *fmt = vdec_formats;
+ unsigned int size = ARRAY_SIZE(vdec_formats);
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].pixfmt == pixfmt)
+ break;
+ }
+
+ if (i == size || fmt[i].type != type)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static const struct venus_format *
+find_format_by_index(unsigned int index, u32 type)
+{
+ const struct venus_format *fmt = vdec_formats;
+ unsigned int size = ARRAY_SIZE(vdec_formats);
+ unsigned int i, k = 0;
+
+ if (index > size)
+ return NULL;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].type != type)
+ continue;
+ if (k == index)
+ break;
+ k++;
+ }
+
+ if (i == size)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static const struct venus_format *
+vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
+ const struct venus_format *fmt;
+ unsigned int p;
+
+ memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
+ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+
+ fmt = find_format(pixmp->pixelformat, f->type);
+ if (!fmt) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ pixmp->pixelformat = V4L2_PIX_FMT_NV12;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ pixmp->pixelformat = V4L2_PIX_FMT_H264;
+ else
+ return NULL;
+ fmt = find_format(pixmp->pixelformat, f->type);
+ pixmp->width = 1280;
+ pixmp->height = 720;
+ }
+
+ pixmp->width = clamp(pixmp->width, inst->cap_width.min,
+ inst->cap_width.max);
+ pixmp->height = clamp(pixmp->height, inst->cap_height.min,
+ inst->cap_height.max);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ pixmp->height = ALIGN(pixmp->height, 32);
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+ pixmp->num_planes = fmt->num_planes;
+ pixmp->flags = 0;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ for (p = 0; p < pixmp->num_planes; p++) {
+ pfmt[p].sizeimage =
+ get_framesize_uncompressed(p, pixmp->width,
+ pixmp->height);
+ pfmt[p].bytesperline = ALIGN(pixmp->width, 128);
+ }
+ } else {
+ pfmt[0].sizeimage = get_framesize_compressed(pixmp->width,
+ pixmp->height);
+ pfmt[0].bytesperline = 0;
+ }
+
+ return fmt;
+}
+
+static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ vdec_try_fmt_common(inst, f);
+
+ return 0;
+}
+
+static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct venus_inst *inst = to_inst(file);
+ const struct venus_format *fmt = NULL;
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fmt = inst->fmt_cap;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ fmt = inst->fmt_out;
+
+ if (inst->reconfig) {
+ struct v4l2_format format = {};
+
+ inst->out_width = inst->reconfig_width;
+ inst->out_height = inst->reconfig_height;
+ inst->reconfig = false;
+
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt;
+ format.fmt.pix_mp.width = inst->out_width;
+ format.fmt.pix_mp.height = inst->out_height;
+
+ vdec_try_fmt_common(inst, &format);
+
+ inst->width = format.fmt.pix_mp.width;
+ inst->height = format.fmt.pix_mp.height;
+ }
+
+ pixmp->pixelformat = fmt->pixfmt;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pixmp->width = inst->width;
+ pixmp->height = inst->height;
+ pixmp->colorspace = inst->colorspace;
+ pixmp->ycbcr_enc = inst->ycbcr_enc;
+ pixmp->quantization = inst->quantization;
+ pixmp->xfer_func = inst->xfer_func;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ pixmp->width = inst->out_width;
+ pixmp->height = inst->out_height;
+ }
+
+ vdec_try_fmt_common(inst, f);
+
+ return 0;
+}
+
+static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct venus_inst *inst = to_inst(file);
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_pix_format_mplane orig_pixmp;
+ const struct venus_format *fmt;
+ struct v4l2_format format;
+ u32 pixfmt_out = 0, pixfmt_cap = 0;
+
+ orig_pixmp = *pixmp;
+
+ fmt = vdec_try_fmt_common(inst, f);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ pixfmt_out = pixmp->pixelformat;
+ pixfmt_cap = inst->fmt_cap->pixfmt;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pixfmt_cap = pixmp->pixelformat;
+ pixfmt_out = inst->fmt_out->pixfmt;
+ }
+
+ memset(&format, 0, sizeof(format));
+
+ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ format.fmt.pix_mp.pixelformat = pixfmt_out;
+ format.fmt.pix_mp.width = orig_pixmp.width;
+ format.fmt.pix_mp.height = orig_pixmp.height;
+ vdec_try_fmt_common(inst, &format);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ inst->out_width = format.fmt.pix_mp.width;
+ inst->out_height = format.fmt.pix_mp.height;
+ inst->colorspace = pixmp->colorspace;
+ inst->ycbcr_enc = pixmp->ycbcr_enc;
+ inst->quantization = pixmp->quantization;
+ inst->xfer_func = pixmp->xfer_func;
+ }
+
+ memset(&format, 0, sizeof(format));
+
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ format.fmt.pix_mp.pixelformat = pixfmt_cap;
+ format.fmt.pix_mp.width = orig_pixmp.width;
+ format.fmt.pix_mp.height = orig_pixmp.height;
+ vdec_try_fmt_common(inst, &format);
+
+ inst->width = format.fmt.pix_mp.width;
+ inst->height = format.fmt.pix_mp.height;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->fmt_out = fmt;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ inst->fmt_cap = fmt;
+
+ return 0;
+}
+
+static int
+vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ s->r.width = inst->out_width;
+ s->r.height = inst->out_height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ s->r.width = inst->width;
+ s->r.height = inst->height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ s->r.width = inst->out_width;
+ s->r.height = inst->out_height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s->r.top = 0;
+ s->r.left = 0;
+
+ return 0;
+}
+
+static int
+vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver));
+ strlcpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ const struct venus_format *fmt;
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+
+ fmt = find_format_by_index(f->index, f->type);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
+
+ return 0;
+}
+
+static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct venus_inst *inst = to_inst(file);
+ struct v4l2_captureparm *cap = &a->parm.capture;
+ struct v4l2_fract *timeperframe = &cap->timeperframe;
+ u64 us_per_frame, fps;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ memset(cap->reserved, 0, sizeof(cap->reserved));
+ if (!timeperframe->denominator)
+ timeperframe->denominator = inst->timeperframe.denominator;
+ if (!timeperframe->numerator)
+ timeperframe->numerator = inst->timeperframe.numerator;
+ cap->readbuffers = 0;
+ cap->extendedmode = 0;
+ cap->capability = V4L2_CAP_TIMEPERFRAME;
+ us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
+ do_div(us_per_frame, timeperframe->denominator);
+
+ if (!us_per_frame)
+ return -EINVAL;
+
+ fps = (u64)USEC_PER_SEC;
+ do_div(fps, us_per_frame);
+
+ inst->fps = fps;
+ inst->timeperframe = *timeperframe;
+
+ return 0;
+}
+
+static int vdec_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct venus_inst *inst = to_inst(file);
+ const struct venus_format *fmt;
+
+ fmt = find_format(fsize->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (!fmt) {
+ fmt = find_format(fsize->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!fmt)
+ return -EINVAL;
+ }
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = inst->cap_width.min;
+ fsize->stepwise.max_width = inst->cap_width.max;
+ fsize->stepwise.step_width = inst->cap_width.step_size;
+ fsize->stepwise.min_height = inst->cap_height.min;
+ fsize->stepwise.max_height = inst->cap_height.max;
+ fsize->stepwise.step_height = inst->cap_height.step_size;
+
+ return 0;
+}
+
+static int vdec_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int
+vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+ if (cmd->cmd != V4L2_DEC_CMD_STOP)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+ struct venus_inst *inst = to_inst(file);
+ int ret;
+
+ ret = vdec_try_decoder_cmd(file, fh, cmd);
+ if (ret)
+ return ret;
+
+ mutex_lock(&inst->lock);
+ inst->cmd_stop = true;
+ mutex_unlock(&inst->lock);
+
+ hfi_session_flush(inst);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops vdec_ioctl_ops = {
+ .vidioc_querycap = vdec_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt,
+ .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt,
+ .vidioc_g_selection = vdec_g_selection,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_s_parm = vdec_s_parm,
+ .vidioc_enum_framesizes = vdec_enum_framesizes,
+ .vidioc_subscribe_event = vdec_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_try_decoder_cmd = vdec_try_decoder_cmd,
+ .vidioc_decoder_cmd = vdec_decoder_cmd,
+};
+
+static int vdec_set_properties(struct venus_inst *inst)
+{
+ struct vdec_controls *ctr = &inst->controls.dec;
+ struct venus_core *core = inst->core;
+ struct hfi_enable en = { .enable = 1 };
+ u32 ptype;
+ int ret;
+
+ if (core->res->hfi_version == HFI_VERSION_1XX) {
+ ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret)
+ return ret;
+ }
+
+ if (core->res->hfi_version == HFI_VERSION_3XX ||
+ inst->cap_bufs_mode_dynamic) {
+ struct hfi_buffer_alloc_mode mode;
+
+ ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
+ mode.type = HFI_BUFFER_OUTPUT;
+ mode.mode = HFI_BUFFER_MODE_DYNAMIC;
+
+ ret = hfi_session_set_property(inst, ptype, &mode);
+ if (ret)
+ return ret;
+ }
+
+ if (ctr->post_loop_deb_mode) {
+ ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
+ en.enable = 1;
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vdec_init_session(struct venus_inst *inst)
+{
+ int ret;
+
+ ret = hfi_session_init(inst, inst->fmt_out->pixfmt);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_set_input_resolution(inst, inst->out_width,
+ inst->out_height);
+ if (ret)
+ goto deinit;
+
+ ret = venus_helper_set_color_format(inst, inst->fmt_cap->pixfmt);
+ if (ret)
+ goto deinit;
+
+ return 0;
+deinit:
+ hfi_session_deinit(inst);
+ return ret;
+}
+
+static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num)
+{
+ struct hfi_buffer_requirements bufreq;
+ int ret;
+
+ ret = vdec_init_session(inst);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
+
+ *num = bufreq.count_actual;
+
+ hfi_session_deinit(inst);
+
+ return ret;
+}
+
+static int vdec_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct venus_inst *inst = vb2_get_drv_priv(q);
+ unsigned int p, num;
+ int ret = 0;
+
+ if (*num_planes) {
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ *num_planes != inst->fmt_out->num_planes)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ *num_planes != inst->fmt_cap->num_planes)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ sizes[0] < inst->input_buf_size)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ sizes[0] < inst->output_buf_size)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ switch (q->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ *num_planes = inst->fmt_out->num_planes;
+ sizes[0] = get_framesize_compressed(inst->out_width,
+ inst->out_height);
+ inst->input_buf_size = sizes[0];
+ inst->num_input_bufs = *num_buffers;
+
+ ret = vdec_cap_num_buffers(inst, &num);
+ if (ret)
+ break;
+
+ inst->num_output_bufs = num;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ *num_planes = inst->fmt_cap->num_planes;
+
+ ret = vdec_cap_num_buffers(inst, &num);
+ if (ret)
+ break;
+
+ *num_buffers = max(*num_buffers, num);
+
+ for (p = 0; p < *num_planes; p++)
+ sizes[p] = get_framesize_uncompressed(p, inst->width,
+ inst->height);
+
+ inst->num_output_bufs = *num_buffers;
+ inst->output_buf_size = sizes[0];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int vdec_verify_conf(struct venus_inst *inst)
+{
+ struct hfi_buffer_requirements bufreq;
+ int ret;
+
+ if (!inst->num_input_bufs || !inst->num_output_bufs)
+ return -EINVAL;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
+ if (ret)
+ return ret;
+
+ if (inst->num_output_bufs < bufreq.count_actual ||
+ inst->num_output_bufs < bufreq.count_min)
+ return -EINVAL;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
+ if (ret)
+ return ret;
+
+ if (inst->num_input_bufs < bufreq.count_min)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(q);
+ struct venus_core *core = inst->core;
+ u32 ptype;
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->streamon_out = 1;
+ else
+ inst->streamon_cap = 1;
+
+ if (!(inst->streamon_out & inst->streamon_cap)) {
+ mutex_unlock(&inst->lock);
+ return 0;
+ }
+
+ venus_helper_init_instance(inst);
+
+ inst->reconfig = false;
+ inst->sequence_cap = 0;
+ inst->sequence_out = 0;
+ inst->cmd_stop = false;
+
+ ret = vdec_init_session(inst);
+ if (ret)
+ goto bufs_done;
+
+ ret = vdec_set_properties(inst);
+ if (ret)
+ goto deinit_sess;
+
+ if (core->res->hfi_version == HFI_VERSION_3XX) {
+ struct hfi_buffer_size_actual buf_sz;
+
+ ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
+ buf_sz.type = HFI_BUFFER_OUTPUT;
+ buf_sz.size = inst->output_buf_size;
+
+ ret = hfi_session_set_property(inst, ptype, &buf_sz);
+ if (ret)
+ goto deinit_sess;
+ }
+
+ ret = vdec_verify_conf(inst);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs,
+ VB2_MAX_FRAME);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venus_helper_vb2_start_streaming(inst);
+ if (ret)
+ goto deinit_sess;
+
+ mutex_unlock(&inst->lock);
+
+ return 0;
+
+deinit_sess:
+ hfi_session_deinit(inst);
+bufs_done:
+ venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->streamon_out = 0;
+ else
+ inst->streamon_cap = 0;
+ mutex_unlock(&inst->lock);
+ return ret;
+}
+
+static const struct vb2_ops vdec_vb2_ops = {
+ .queue_setup = vdec_queue_setup,
+ .buf_init = venus_helper_vb2_buf_init,
+ .buf_prepare = venus_helper_vb2_buf_prepare,
+ .start_streaming = vdec_start_streaming,
+ .stop_streaming = venus_helper_vb2_stop_streaming,
+ .buf_queue = venus_helper_vb2_buf_queue,
+};
+
+static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
+ u32 tag, u32 bytesused, u32 data_offset, u32 flags,
+ u32 hfi_flags, u64 timestamp_us)
+{
+ enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
+ struct vb2_v4l2_buffer *vbuf;
+ struct vb2_buffer *vb;
+ unsigned int type;
+
+ if (buf_type == HFI_BUFFER_INPUT)
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ else
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ vbuf = venus_helper_find_buf(inst, type, tag);
+ if (!vbuf)
+ return;
+
+ vbuf->flags = flags;
+ vbuf->field = V4L2_FIELD_NONE;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ vb = &vbuf->vb2_buf;
+ vb->planes[0].bytesused =
+ max_t(unsigned int, inst->output_buf_size, bytesused);
+ vb->planes[0].data_offset = data_offset;
+ vb->timestamp = timestamp_us * NSEC_PER_USEC;
+ vbuf->sequence = inst->sequence_cap++;
+
+ if (inst->cmd_stop) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ inst->cmd_stop = false;
+ }
+
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
+ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
+
+ v4l2_event_queue_fh(&inst->fh, &ev);
+ }
+ } else {
+ vbuf->sequence = inst->sequence_out++;
+ }
+
+ if (hfi_flags & HFI_BUFFERFLAG_READONLY)
+ venus_helper_acquire_buf_ref(vbuf);
+
+ if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT)
+ state = VB2_BUF_STATE_ERROR;
+
+ v4l2_m2m_buf_done(vbuf, state);
+}
+
+static void vdec_event_notify(struct venus_inst *inst, u32 event,
+ struct hfi_event_data *data)
+{
+ struct venus_core *core = inst->core;
+ struct device *dev = core->dev_dec;
+ static const struct v4l2_event ev = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION };
+
+ switch (event) {
+ case EVT_SESSION_ERROR:
+ inst->session_error = true;
+ dev_err(dev, "dec: event session error %x\n", inst->error);
+ break;
+ case EVT_SYS_EVENT_CHANGE:
+ switch (data->event_type) {
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
+ hfi_session_continue(inst);
+ dev_dbg(dev, "event sufficient resources\n");
+ break;
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
+ inst->reconfig_height = data->height;
+ inst->reconfig_width = data->width;
+ inst->reconfig = true;
+
+ v4l2_event_queue_fh(&inst->fh, &ev);
+
+ dev_dbg(dev, "event not sufficient resources (%ux%u)\n",
+ data->width, data->height);
+ break;
+ case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
+ venus_helper_release_buf_ref(inst, data->tag);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct hfi_inst_ops vdec_hfi_ops = {
+ .buf_done = vdec_buf_done,
+ .event_notify = vdec_event_notify,
+};
+
+static void vdec_inst_init(struct venus_inst *inst)
+{
+ inst->fmt_out = &vdec_formats[6];
+ inst->fmt_cap = &vdec_formats[0];
+ inst->width = 1280;
+ inst->height = ALIGN(720, 32);
+ inst->out_width = 1280;
+ inst->out_height = 720;
+ inst->fps = 30;
+ inst->timeperframe.numerator = 1;
+ inst->timeperframe.denominator = 30;
+
+ inst->cap_width.min = 64;
+ inst->cap_width.max = 1920;
+ if (inst->core->res->hfi_version == HFI_VERSION_3XX)
+ inst->cap_width.max = 3840;
+ inst->cap_width.step_size = 1;
+ inst->cap_height.min = 64;
+ inst->cap_height.max = ALIGN(1080, 32);
+ if (inst->core->res->hfi_version == HFI_VERSION_3XX)
+ inst->cap_height.max = ALIGN(2160, 32);
+ inst->cap_height.step_size = 1;
+ inst->cap_framerate.min = 1;
+ inst->cap_framerate.max = 30;
+ inst->cap_framerate.step_size = 1;
+ inst->cap_mbs_per_frame.min = 16;
+ inst->cap_mbs_per_frame.max = 8160;
+}
+
+static const struct v4l2_m2m_ops vdec_m2m_ops = {
+ .device_run = venus_helper_m2m_device_run,
+ .job_abort = venus_helper_m2m_job_abort,
+};
+
+static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct venus_inst *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = &vdec_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->drv_priv = inst;
+ src_vq->buf_struct_size = sizeof(struct venus_buffer);
+ src_vq->allow_zero_bytesused = 1;
+ src_vq->min_buffers_needed = 1;
+ src_vq->dev = inst->core->dev;
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = &vdec_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_sg_memops;
+ dst_vq->drv_priv = inst;
+ dst_vq->buf_struct_size = sizeof(struct venus_buffer);
+ dst_vq->allow_zero_bytesused = 1;
+ dst_vq->min_buffers_needed = 1;
+ dst_vq->dev = inst->core->dev;
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ vb2_queue_release(src_vq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vdec_open(struct file *file)
+{
+ struct venus_core *core = video_drvdata(file);
+ struct venus_inst *inst;
+ int ret;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&inst->registeredbufs);
+ INIT_LIST_HEAD(&inst->internalbufs);
+ INIT_LIST_HEAD(&inst->list);
+ mutex_init(&inst->lock);
+
+ inst->core = core;
+ inst->session_type = VIDC_SESSION_TYPE_DEC;
+ inst->num_output_bufs = 1;
+
+ venus_helper_init_instance(inst);
+
+ ret = pm_runtime_get_sync(core->dev_dec);
+ if (ret < 0)
+ goto err_free_inst;
+
+ ret = vdec_ctrl_init(inst);
+ if (ret)
+ goto err_put_sync;
+
+ ret = hfi_session_create(inst, &vdec_hfi_ops);
+ if (ret)
+ goto err_ctrl_deinit;
+
+ vdec_inst_init(inst);
+
+ /*
+ * create m2m device for every instance, the m2m context scheduling
+ * is made by firmware side so we do not need to care about.
+ */
+ inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(inst->m2m_dev)) {
+ ret = PTR_ERR(inst->m2m_dev);
+ goto err_session_destroy;
+ }
+
+ inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init);
+ if (IS_ERR(inst->m2m_ctx)) {
+ ret = PTR_ERR(inst->m2m_ctx);
+ goto err_m2m_release;
+ }
+
+ v4l2_fh_init(&inst->fh, core->vdev_dec);
+
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ v4l2_fh_add(&inst->fh);
+ inst->fh.m2m_ctx = inst->m2m_ctx;
+ file->private_data = &inst->fh;
+
+ return 0;
+
+err_m2m_release:
+ v4l2_m2m_release(inst->m2m_dev);
+err_session_destroy:
+ hfi_session_destroy(inst);
+err_ctrl_deinit:
+ vdec_ctrl_deinit(inst);
+err_put_sync:
+ pm_runtime_put_sync(core->dev_dec);
+err_free_inst:
+ kfree(inst);
+ return ret;
+}
+
+static int vdec_close(struct file *file)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+ v4l2_m2m_release(inst->m2m_dev);
+ vdec_ctrl_deinit(inst);
+ hfi_session_destroy(inst);
+ mutex_destroy(&inst->lock);
+ v4l2_fh_del(&inst->fh);
+ v4l2_fh_exit(&inst->fh);
+
+ pm_runtime_put_sync(inst->core->dev_dec);
+
+ kfree(inst);
+ return 0;
+}
+
+static const struct v4l2_file_operations vdec_fops = {
+ .owner = THIS_MODULE,
+ .open = vdec_open,
+ .release = vdec_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = v4l2_compat_ioctl32,
+#endif
+};
+
+static int vdec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct video_device *vdev;
+ struct venus_core *core;
+ int ret;
+
+ if (!dev->parent)
+ return -EPROBE_DEFER;
+
+ core = dev_get_drvdata(dev->parent);
+ if (!core)
+ return -EPROBE_DEFER;
+
+ if (core->res->hfi_version == HFI_VERSION_3XX) {
+ core->core0_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(core->core0_clk))
+ return PTR_ERR(core->core0_clk);
+ }
+
+ platform_set_drvdata(pdev, core);
+
+ vdev = video_device_alloc();
+ if (!vdev)
+ return -ENOMEM;
+
+ vdev->release = video_device_release;
+ vdev->fops = &vdec_fops;
+ vdev->ioctl_ops = &vdec_ioctl_ops;
+ vdev->vfl_dir = VFL_DIR_M2M;
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vdev_release;
+
+ core->vdev_dec = vdev;
+ core->dev_dec = dev;
+
+ video_set_drvdata(vdev, core);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+err_vdev_release:
+ video_device_release(vdev);
+ return ret;
+}
+
+static int vdec_remove(struct platform_device *pdev)
+{
+ struct venus_core *core = dev_get_drvdata(pdev->dev.parent);
+
+ video_unregister_device(core->vdev_dec);
+ pm_runtime_disable(core->dev_dec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int vdec_runtime_suspend(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ if (core->res->hfi_version == HFI_VERSION_1XX)
+ return 0;
+
+ writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+ clk_disable_unprepare(core->core0_clk);
+ writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+
+ return 0;
+}
+
+static int vdec_runtime_resume(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ if (core->res->hfi_version == HFI_VERSION_1XX)
+ return 0;
+
+ writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+ ret = clk_prepare_enable(core->core0_clk);
+ writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops vdec_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(vdec_runtime_suspend, vdec_runtime_resume, NULL)
+};
+
+static const struct of_device_id vdec_dt_match[] = {
+ { .compatible = "venus-decoder" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vdec_dt_match);
+
+static struct platform_driver qcom_venus_dec_driver = {
+ .probe = vdec_probe,
+ .remove = vdec_remove,
+ .driver = {
+ .name = "qcom-venus-decoder",
+ .of_match_table = vdec_dt_match,
+ .pm = &vdec_pm_ops,
+ },
+};
+module_platform_driver(qcom_venus_dec_driver);
+
+MODULE_ALIAS("platform:qcom-venus-decoder");
+MODULE_DESCRIPTION("Qualcomm Venus video decoder driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/venus/vdec.h b/drivers/media/platform/qcom/venus/vdec.h
new file mode 100644
index 000000000000..84b672c54d02
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/vdec.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_VDEC_H__
+#define __VENUS_VDEC_H__
+
+struct venus_inst;
+
+int vdec_ctrl_init(struct venus_inst *inst);
+void vdec_ctrl_deinit(struct venus_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c
new file mode 100644
index 000000000000..032839bbc967
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+
+#include "core.h"
+#include "vdec.h"
+
+static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct venus_inst *inst = ctrl_to_inst(ctrl);
+ struct vdec_controls *ctr = &inst->controls.dec;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctr->post_loop_deb_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ ctr->profile = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ ctr->level = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct venus_inst *inst = ctrl_to_inst(ctrl);
+ struct vdec_controls *ctr = &inst->controls.dec;
+ union hfi_get_property hprop;
+ u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ ret = hfi_session_get_property(inst, ptype, &hprop);
+ if (!ret)
+ ctr->profile = hprop.profile_level.profile;
+ ctrl->val = ctr->profile;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ ret = hfi_session_get_property(inst, ptype, &hprop);
+ if (!ret)
+ ctr->level = hprop.profile_level.level;
+ ctrl->val = ctr->level;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctrl->val = ctr->post_loop_deb_mode;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ ctrl->val = inst->num_output_bufs;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops vdec_ctrl_ops = {
+ .s_ctrl = vdec_op_s_ctrl,
+ .g_volatile_ctrl = vdec_op_g_volatile_ctrl,
+};
+
+int vdec_ctrl_init(struct venus_inst *inst)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 7);
+ if (ret)
+ return ret;
+
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY,
+ ~((1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE) |
+ (1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE)),
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+ 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH)),
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ 0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, 0, 1, 1, 0);
+
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ret = inst->ctrl_handler.error;
+ if (ret) {
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+ }
+
+ return 0;
+}
+
+void vdec_ctrl_deinit(struct venus_inst *inst)
+{
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+}
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
new file mode 100644
index 000000000000..39748e7a08e4
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+
+#include "hfi_venus_io.h"
+#include "core.h"
+#include "helpers.h"
+#include "venc.h"
+
+#define NUM_B_FRAMES_MAX 4
+
+static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height)
+{
+ u32 y_stride, uv_stride, y_plane;
+ u32 y_sclines, uv_sclines, uv_plane;
+ u32 size;
+
+ y_stride = ALIGN(width, 128);
+ uv_stride = ALIGN(width, 128);
+ y_sclines = ALIGN(height, 32);
+ uv_sclines = ALIGN(((height + 1) >> 1), 16);
+
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines + SZ_4K;
+ size = y_plane + uv_plane + SZ_8K;
+ size = ALIGN(size, SZ_4K);
+
+ return size;
+}
+
+static u32 get_framesize_compressed(u32 width, u32 height)
+{
+ u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2;
+
+ return ALIGN(sz, SZ_4K);
+}
+
+/*
+ * Three resons to keep MPLANE formats (despite that the number of planes
+ * currently is one):
+ * - the MPLANE formats allow only one plane to be used
+ * - the downstream driver use MPLANE formats too
+ * - future firmware versions could add support for >1 planes
+ */
+static const struct venus_format venc_formats[] = {
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VP8,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+};
+
+static const struct venus_format *find_format(u32 pixfmt, u32 type)
+{
+ const struct venus_format *fmt = venc_formats;
+ unsigned int size = ARRAY_SIZE(venc_formats);
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].pixfmt == pixfmt)
+ break;
+ }
+
+ if (i == size || fmt[i].type != type)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static const struct venus_format *
+find_format_by_index(unsigned int index, u32 type)
+{
+ const struct venus_format *fmt = venc_formats;
+ unsigned int size = ARRAY_SIZE(venc_formats);
+ unsigned int i, k = 0;
+
+ if (index > size)
+ return NULL;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].type != type)
+ continue;
+ if (k == index)
+ break;
+ k++;
+ }
+
+ if (i == size)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static int venc_v4l2_to_hfi(int id, int value)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0:
+ default:
+ return HFI_MPEG4_LEVEL_0;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B:
+ return HFI_MPEG4_LEVEL_0b;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1:
+ return HFI_MPEG4_LEVEL_1;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2:
+ return HFI_MPEG4_LEVEL_2;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3:
+ return HFI_MPEG4_LEVEL_3;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4:
+ return HFI_MPEG4_LEVEL_4;
+ case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5:
+ return HFI_MPEG4_LEVEL_5;
+ }
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+ default:
+ return HFI_MPEG4_PROFILE_SIMPLE;
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+ return HFI_MPEG4_PROFILE_ADVANCEDSIMPLE;
+ }
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ return HFI_H264_PROFILE_BASELINE;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+ return HFI_H264_PROFILE_CONSTRAINED_BASE;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ return HFI_H264_PROFILE_MAIN;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ default:
+ return HFI_H264_PROFILE_HIGH;
+ }
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ return HFI_H264_LEVEL_1;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+ return HFI_H264_LEVEL_1b;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ return HFI_H264_LEVEL_11;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ return HFI_H264_LEVEL_12;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ return HFI_H264_LEVEL_13;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ return HFI_H264_LEVEL_2;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ return HFI_H264_LEVEL_21;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ return HFI_H264_LEVEL_22;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ return HFI_H264_LEVEL_3;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ return HFI_H264_LEVEL_31;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ return HFI_H264_LEVEL_32;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ return HFI_H264_LEVEL_4;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ return HFI_H264_LEVEL_41;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ return HFI_H264_LEVEL_42;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ default:
+ return HFI_H264_LEVEL_5;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ return HFI_H264_LEVEL_51;
+ }
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC:
+ default:
+ return HFI_H264_ENTROPY_CAVLC;
+ case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
+ return HFI_H264_ENTROPY_CABAC;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ switch (value) {
+ case 0:
+ default:
+ return HFI_VPX_PROFILE_VERSION_0;
+ case 1:
+ return HFI_VPX_PROFILE_VERSION_1;
+ case 2:
+ return HFI_VPX_PROFILE_VERSION_2;
+ case 3:
+ return HFI_VPX_PROFILE_VERSION_3;
+ }
+ }
+
+ return 0;
+}
+
+static int
+venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, "qcom-venus", sizeof(cap->driver));
+ strlcpy(cap->card, "Qualcomm Venus video encoder", sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ const struct venus_format *fmt;
+
+ fmt = find_format_by_index(f->index, f->type);
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
+
+ return 0;
+}
+
+static const struct venus_format *
+venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
+ const struct venus_format *fmt;
+ unsigned int p;
+
+ memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
+ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+
+ fmt = find_format(pixmp->pixelformat, f->type);
+ if (!fmt) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ pixmp->pixelformat = V4L2_PIX_FMT_H264;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ pixmp->pixelformat = V4L2_PIX_FMT_NV12;
+ else
+ return NULL;
+ fmt = find_format(pixmp->pixelformat, f->type);
+ pixmp->width = 1280;
+ pixmp->height = 720;
+ }
+
+ pixmp->width = clamp(pixmp->width, inst->cap_width.min,
+ inst->cap_width.max);
+ pixmp->height = clamp(pixmp->height, inst->cap_height.min,
+ inst->cap_height.max);
+
+ if (inst->core->res->hfi_version == HFI_VERSION_1XX)
+ pixmp->height = ALIGN(pixmp->height, 32);
+
+ pixmp->width = ALIGN(pixmp->width, 2);
+ pixmp->height = ALIGN(pixmp->height, 2);
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+ pixmp->num_planes = fmt->num_planes;
+ pixmp->flags = 0;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ for (p = 0; p < pixmp->num_planes; p++) {
+ pfmt[p].sizeimage =
+ get_framesize_uncompressed(p, pixmp->width,
+ pixmp->height);
+
+ pfmt[p].bytesperline = ALIGN(pixmp->width, 128);
+ }
+ } else {
+ pfmt[0].sizeimage = get_framesize_compressed(pixmp->width,
+ pixmp->height);
+ pfmt[0].bytesperline = 0;
+ }
+
+ return fmt;
+}
+
+static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ venc_try_fmt_common(inst, f);
+
+ return 0;
+}
+
+static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct venus_inst *inst = to_inst(file);
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_pix_format_mplane orig_pixmp;
+ const struct venus_format *fmt;
+ struct v4l2_format format;
+ u32 pixfmt_out = 0, pixfmt_cap = 0;
+
+ orig_pixmp = *pixmp;
+
+ fmt = venc_try_fmt_common(inst, f);
+ if (!fmt)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ pixfmt_out = pixmp->pixelformat;
+ pixfmt_cap = inst->fmt_cap->pixfmt;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pixfmt_cap = pixmp->pixelformat;
+ pixfmt_out = inst->fmt_out->pixfmt;
+ }
+
+ memset(&format, 0, sizeof(format));
+
+ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ format.fmt.pix_mp.pixelformat = pixfmt_out;
+ format.fmt.pix_mp.width = orig_pixmp.width;
+ format.fmt.pix_mp.height = orig_pixmp.height;
+ venc_try_fmt_common(inst, &format);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ inst->out_width = format.fmt.pix_mp.width;
+ inst->out_height = format.fmt.pix_mp.height;
+ inst->colorspace = pixmp->colorspace;
+ inst->ycbcr_enc = pixmp->ycbcr_enc;
+ inst->quantization = pixmp->quantization;
+ inst->xfer_func = pixmp->xfer_func;
+ }
+
+ memset(&format, 0, sizeof(format));
+
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ format.fmt.pix_mp.pixelformat = pixfmt_cap;
+ format.fmt.pix_mp.width = orig_pixmp.width;
+ format.fmt.pix_mp.height = orig_pixmp.height;
+ venc_try_fmt_common(inst, &format);
+
+ inst->width = format.fmt.pix_mp.width;
+ inst->height = format.fmt.pix_mp.height;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->fmt_out = fmt;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ inst->fmt_cap = fmt;
+
+ return 0;
+}
+
+static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct venus_inst *inst = to_inst(file);
+ const struct venus_format *fmt;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fmt = inst->fmt_cap;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ fmt = inst->fmt_out;
+ else
+ return -EINVAL;
+
+ pixmp->pixelformat = fmt->pixfmt;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ pixmp->width = inst->width;
+ pixmp->height = inst->height;
+ pixmp->colorspace = inst->colorspace;
+ pixmp->ycbcr_enc = inst->ycbcr_enc;
+ pixmp->quantization = inst->quantization;
+ pixmp->xfer_func = inst->xfer_func;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ pixmp->width = inst->out_width;
+ pixmp->height = inst->out_height;
+ }
+
+ venc_try_fmt_common(inst, f);
+
+ return 0;
+}
+
+static int
+venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.width = inst->width;
+ s->r.height = inst->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ s->r.width = inst->out_width;
+ s->r.height = inst->out_height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s->r.top = 0;
+ s->r.left = 0;
+
+ return 0;
+}
+
+static int
+venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ if (s->r.width != inst->out_width ||
+ s->r.height != inst->out_height ||
+ s->r.top != 0 || s->r.left != 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct venus_inst *inst = to_inst(file);
+ struct v4l2_outputparm *out = &a->parm.output;
+ struct v4l2_fract *timeperframe = &out->timeperframe;
+ u64 us_per_frame, fps;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ memset(out->reserved, 0, sizeof(out->reserved));
+
+ if (!timeperframe->denominator)
+ timeperframe->denominator = inst->timeperframe.denominator;
+ if (!timeperframe->numerator)
+ timeperframe->numerator = inst->timeperframe.numerator;
+
+ out->capability = V4L2_CAP_TIMEPERFRAME;
+
+ us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
+ do_div(us_per_frame, timeperframe->denominator);
+
+ if (!us_per_frame)
+ return -EINVAL;
+
+ fps = (u64)USEC_PER_SEC;
+ do_div(fps, us_per_frame);
+
+ inst->timeperframe = *timeperframe;
+ inst->fps = fps;
+
+ return 0;
+}
+
+static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ a->parm.output.capability |= V4L2_CAP_TIMEPERFRAME;
+ a->parm.output.timeperframe = inst->timeperframe;
+
+ return 0;
+}
+
+static int venc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct venus_inst *inst = to_inst(file);
+ const struct venus_format *fmt;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fmt = find_format(fsize->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (!fmt) {
+ fmt = find_format(fsize->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!fmt)
+ return -EINVAL;
+ }
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fsize->stepwise.min_width = inst->cap_width.min;
+ fsize->stepwise.max_width = inst->cap_width.max;
+ fsize->stepwise.step_width = inst->cap_width.step_size;
+ fsize->stepwise.min_height = inst->cap_height.min;
+ fsize->stepwise.max_height = inst->cap_height.max;
+ fsize->stepwise.step_height = inst->cap_height.step_size;
+
+ return 0;
+}
+
+static int venc_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct venus_inst *inst = to_inst(file);
+ const struct venus_format *fmt;
+
+ fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+
+ fmt = find_format(fival->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (!fmt) {
+ fmt = find_format(fival->pixel_format,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!fmt)
+ return -EINVAL;
+ }
+
+ if (fival->index)
+ return -EINVAL;
+
+ if (!fival->width || !fival->height)
+ return -EINVAL;
+
+ if (fival->width > inst->cap_width.max ||
+ fival->width < inst->cap_width.min ||
+ fival->height > inst->cap_height.max ||
+ fival->height < inst->cap_height.min)
+ return -EINVAL;
+
+ fival->stepwise.min.numerator = 1;
+ fival->stepwise.min.denominator = inst->cap_framerate.max;
+ fival->stepwise.max.numerator = 1;
+ fival->stepwise.max.denominator = inst->cap_framerate.min;
+ fival->stepwise.step.numerator = 1;
+ fival->stepwise.step.denominator = inst->cap_framerate.max;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops venc_ioctl_ops = {
+ .vidioc_querycap = venc_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = venc_enum_fmt,
+ .vidioc_enum_fmt_vid_out_mplane = venc_enum_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = venc_s_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = venc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = venc_try_fmt,
+ .vidioc_g_selection = venc_g_selection,
+ .vidioc_s_selection = venc_s_selection,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_s_parm = venc_s_parm,
+ .vidioc_g_parm = venc_g_parm,
+ .vidioc_enum_framesizes = venc_enum_framesizes,
+ .vidioc_enum_frameintervals = venc_enum_frameintervals,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int venc_set_properties(struct venus_inst *inst)
+{
+ struct venc_controls *ctr = &inst->controls.enc;
+ struct hfi_intra_period intra_period;
+ struct hfi_profile_level pl;
+ struct hfi_framerate frate;
+ struct hfi_bitrate brate;
+ struct hfi_idr_period idrp;
+ u32 ptype, rate_control, bitrate, profile = 0, level = 0;
+ int ret;
+
+ ptype = HFI_PROPERTY_CONFIG_FRAME_RATE;
+ frate.buffer_type = HFI_BUFFER_OUTPUT;
+ frate.framerate = inst->fps * (1 << 16);
+
+ ret = hfi_session_set_property(inst, ptype, &frate);
+ if (ret)
+ return ret;
+
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) {
+ struct hfi_h264_vui_timing_info info;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO;
+ info.enable = 1;
+ info.fixed_framerate = 1;
+ info.time_scale = NSEC_PER_SEC;
+
+ ret = hfi_session_set_property(inst, ptype, &info);
+ if (ret)
+ return ret;
+ }
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
+ idrp.idr_period = ctr->gop_size;
+ ret = hfi_session_set_property(inst, ptype, &idrp);
+ if (ret)
+ return ret;
+
+ if (ctr->num_b_frames) {
+ u32 max_num_b_frames = NUM_B_FRAMES_MAX;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES;
+ ret = hfi_session_set_property(inst, ptype, &max_num_b_frames);
+ if (ret)
+ return ret;
+ }
+
+ /* intra_period = pframes + bframes + 1 */
+ if (!ctr->num_p_frames)
+ ctr->num_p_frames = 2 * 15 - 1,
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD;
+ intra_period.pframes = ctr->num_p_frames;
+ intra_period.bframes = ctr->num_b_frames;
+
+ ret = hfi_session_set_property(inst, ptype, &intra_period);
+ if (ret)
+ return ret;
+
+ if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ rate_control = HFI_RATE_CONTROL_VBR_CFR;
+ else
+ rate_control = HFI_RATE_CONTROL_CBR_CFR;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL;
+ ret = hfi_session_set_property(inst, ptype, &rate_control);
+ if (ret)
+ return ret;
+
+ if (!ctr->bitrate)
+ bitrate = 64000;
+ else
+ bitrate = ctr->bitrate;
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+ brate.bitrate = bitrate;
+ brate.layer_id = 0;
+
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ if (ret)
+ return ret;
+
+ if (!ctr->bitrate_peak)
+ bitrate *= 2;
+ else
+ bitrate = ctr->bitrate_peak;
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
+ brate.bitrate = bitrate;
+ brate.layer_id = 0;
+
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ if (ret)
+ return ret;
+
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) {
+ profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ ctr->profile.h264);
+ level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ ctr->level.h264);
+ } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) {
+ profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE,
+ ctr->profile.vpx);
+ level = 0;
+ } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) {
+ profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ ctr->profile.mpeg4);
+ level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ ctr->level.mpeg4);
+ } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) {
+ profile = 0;
+ level = 0;
+ }
+
+ ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+ pl.profile = profile;
+ pl.level = level;
+
+ ret = hfi_session_set_property(inst, ptype, &pl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int venc_init_session(struct venus_inst *inst)
+{
+ int ret;
+
+ ret = hfi_session_init(inst, inst->fmt_cap->pixfmt);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_set_input_resolution(inst, inst->out_width,
+ inst->out_height);
+ if (ret)
+ goto deinit;
+
+ ret = venus_helper_set_output_resolution(inst, inst->width,
+ inst->height);
+ if (ret)
+ goto deinit;
+
+ ret = venus_helper_set_color_format(inst, inst->fmt_out->pixfmt);
+ if (ret)
+ goto deinit;
+
+ return 0;
+deinit:
+ hfi_session_deinit(inst);
+ return ret;
+}
+
+static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num)
+{
+ struct hfi_buffer_requirements bufreq;
+ int ret;
+
+ ret = venc_init_session(inst);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
+
+ *num = bufreq.count_actual;
+
+ hfi_session_deinit(inst);
+
+ return ret;
+}
+
+static int venc_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct venus_inst *inst = vb2_get_drv_priv(q);
+ unsigned int p, num, min = 4;
+ int ret = 0;
+
+ if (*num_planes) {
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ *num_planes != inst->fmt_out->num_planes)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ *num_planes != inst->fmt_cap->num_planes)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ sizes[0] < inst->input_buf_size)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ sizes[0] < inst->output_buf_size)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ switch (q->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ *num_planes = inst->fmt_out->num_planes;
+
+ ret = venc_out_num_buffers(inst, &num);
+ if (ret)
+ break;
+
+ num = max(num, min);
+ *num_buffers = max(*num_buffers, num);
+ inst->num_input_bufs = *num_buffers;
+
+ for (p = 0; p < *num_planes; ++p)
+ sizes[p] = get_framesize_uncompressed(p, inst->width,
+ inst->height);
+ inst->input_buf_size = sizes[0];
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ *num_planes = inst->fmt_cap->num_planes;
+ *num_buffers = max(*num_buffers, min);
+ inst->num_output_bufs = *num_buffers;
+ sizes[0] = get_framesize_compressed(inst->width, inst->height);
+ inst->output_buf_size = sizes[0];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int venc_verify_conf(struct venus_inst *inst)
+{
+ struct hfi_buffer_requirements bufreq;
+ int ret;
+
+ if (!inst->num_input_bufs || !inst->num_output_bufs)
+ return -EINVAL;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
+ if (ret)
+ return ret;
+
+ if (inst->num_output_bufs < bufreq.count_actual ||
+ inst->num_output_bufs < bufreq.count_min)
+ return -EINVAL;
+
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
+ if (ret)
+ return ret;
+
+ if (inst->num_input_bufs < bufreq.count_actual ||
+ inst->num_input_bufs < bufreq.count_min)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int venc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct venus_inst *inst = vb2_get_drv_priv(q);
+ int ret;
+
+ mutex_lock(&inst->lock);
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->streamon_out = 1;
+ else
+ inst->streamon_cap = 1;
+
+ if (!(inst->streamon_out & inst->streamon_cap)) {
+ mutex_unlock(&inst->lock);
+ return 0;
+ }
+
+ venus_helper_init_instance(inst);
+
+ inst->sequence_cap = 0;
+ inst->sequence_out = 0;
+
+ ret = venc_init_session(inst);
+ if (ret)
+ goto bufs_done;
+
+ ret = venc_set_properties(inst);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venc_verify_conf(inst);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs,
+ inst->num_output_bufs);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venus_helper_vb2_start_streaming(inst);
+ if (ret)
+ goto deinit_sess;
+
+ mutex_unlock(&inst->lock);
+
+ return 0;
+
+deinit_sess:
+ hfi_session_deinit(inst);
+bufs_done:
+ venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ inst->streamon_out = 0;
+ else
+ inst->streamon_cap = 0;
+ mutex_unlock(&inst->lock);
+ return ret;
+}
+
+static const struct vb2_ops venc_vb2_ops = {
+ .queue_setup = venc_queue_setup,
+ .buf_init = venus_helper_vb2_buf_init,
+ .buf_prepare = venus_helper_vb2_buf_prepare,
+ .start_streaming = venc_start_streaming,
+ .stop_streaming = venus_helper_vb2_stop_streaming,
+ .buf_queue = venus_helper_vb2_buf_queue,
+};
+
+static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type,
+ u32 tag, u32 bytesused, u32 data_offset, u32 flags,
+ u32 hfi_flags, u64 timestamp_us)
+{
+ struct vb2_v4l2_buffer *vbuf;
+ struct vb2_buffer *vb;
+ unsigned int type;
+
+ if (buf_type == HFI_BUFFER_INPUT)
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ else
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ vbuf = venus_helper_find_buf(inst, type, tag);
+ if (!vbuf)
+ return;
+
+ vb = &vbuf->vb2_buf;
+ vb->planes[0].bytesused = bytesused;
+ vb->planes[0].data_offset = data_offset;
+
+ vbuf->flags = flags;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ vb->timestamp = timestamp_us * NSEC_PER_USEC;
+ vbuf->sequence = inst->sequence_cap++;
+ } else {
+ vbuf->sequence = inst->sequence_out++;
+ }
+
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+}
+
+static void venc_event_notify(struct venus_inst *inst, u32 event,
+ struct hfi_event_data *data)
+{
+ struct device *dev = inst->core->dev_enc;
+
+ if (event == EVT_SESSION_ERROR) {
+ inst->session_error = true;
+ dev_err(dev, "enc: event session error %x\n", inst->error);
+ }
+}
+
+static const struct hfi_inst_ops venc_hfi_ops = {
+ .buf_done = venc_buf_done,
+ .event_notify = venc_event_notify,
+};
+
+static const struct v4l2_m2m_ops venc_m2m_ops = {
+ .device_run = venus_helper_m2m_device_run,
+ .job_abort = venus_helper_m2m_job_abort,
+};
+
+static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct venus_inst *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = &venc_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->drv_priv = inst;
+ src_vq->buf_struct_size = sizeof(struct venus_buffer);
+ src_vq->allow_zero_bytesused = 1;
+ src_vq->min_buffers_needed = 1;
+ src_vq->dev = inst->core->dev;
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = &venc_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_sg_memops;
+ dst_vq->drv_priv = inst;
+ dst_vq->buf_struct_size = sizeof(struct venus_buffer);
+ dst_vq->allow_zero_bytesused = 1;
+ dst_vq->min_buffers_needed = 1;
+ dst_vq->dev = inst->core->dev;
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ vb2_queue_release(src_vq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void venc_inst_init(struct venus_inst *inst)
+{
+ inst->fmt_cap = &venc_formats[2];
+ inst->fmt_out = &venc_formats[0];
+ inst->width = 1280;
+ inst->height = ALIGN(720, 32);
+ inst->out_width = 1280;
+ inst->out_height = 720;
+ inst->fps = 15;
+ inst->timeperframe.numerator = 1;
+ inst->timeperframe.denominator = 15;
+
+ inst->cap_width.min = 96;
+ inst->cap_width.max = 1920;
+ if (inst->core->res->hfi_version == HFI_VERSION_3XX)
+ inst->cap_width.max = 3840;
+ inst->cap_width.step_size = 2;
+ inst->cap_height.min = 64;
+ inst->cap_height.max = ALIGN(1080, 32);
+ if (inst->core->res->hfi_version == HFI_VERSION_3XX)
+ inst->cap_height.max = ALIGN(2160, 32);
+ inst->cap_height.step_size = 2;
+ inst->cap_framerate.min = 1;
+ inst->cap_framerate.max = 30;
+ inst->cap_framerate.step_size = 1;
+ inst->cap_mbs_per_frame.min = 24;
+ inst->cap_mbs_per_frame.max = 8160;
+}
+
+static int venc_open(struct file *file)
+{
+ struct venus_core *core = video_drvdata(file);
+ struct venus_inst *inst;
+ int ret;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&inst->registeredbufs);
+ INIT_LIST_HEAD(&inst->internalbufs);
+ INIT_LIST_HEAD(&inst->list);
+ mutex_init(&inst->lock);
+
+ inst->core = core;
+ inst->session_type = VIDC_SESSION_TYPE_ENC;
+
+ venus_helper_init_instance(inst);
+
+ ret = pm_runtime_get_sync(core->dev_enc);
+ if (ret < 0)
+ goto err_free_inst;
+
+ ret = venc_ctrl_init(inst);
+ if (ret)
+ goto err_put_sync;
+
+ ret = hfi_session_create(inst, &venc_hfi_ops);
+ if (ret)
+ goto err_ctrl_deinit;
+
+ venc_inst_init(inst);
+
+ /*
+ * create m2m device for every instance, the m2m context scheduling
+ * is made by firmware side so we do not need to care about.
+ */
+ inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops);
+ if (IS_ERR(inst->m2m_dev)) {
+ ret = PTR_ERR(inst->m2m_dev);
+ goto err_session_destroy;
+ }
+
+ inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init);
+ if (IS_ERR(inst->m2m_ctx)) {
+ ret = PTR_ERR(inst->m2m_ctx);
+ goto err_m2m_release;
+ }
+
+ v4l2_fh_init(&inst->fh, core->vdev_enc);
+
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ v4l2_fh_add(&inst->fh);
+ inst->fh.m2m_ctx = inst->m2m_ctx;
+ file->private_data = &inst->fh;
+
+ return 0;
+
+err_m2m_release:
+ v4l2_m2m_release(inst->m2m_dev);
+err_session_destroy:
+ hfi_session_destroy(inst);
+err_ctrl_deinit:
+ venc_ctrl_deinit(inst);
+err_put_sync:
+ pm_runtime_put_sync(core->dev_enc);
+err_free_inst:
+ kfree(inst);
+ return ret;
+}
+
+static int venc_close(struct file *file)
+{
+ struct venus_inst *inst = to_inst(file);
+
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+ v4l2_m2m_release(inst->m2m_dev);
+ venc_ctrl_deinit(inst);
+ hfi_session_destroy(inst);
+ mutex_destroy(&inst->lock);
+ v4l2_fh_del(&inst->fh);
+ v4l2_fh_exit(&inst->fh);
+
+ pm_runtime_put_sync(inst->core->dev_enc);
+
+ kfree(inst);
+ return 0;
+}
+
+static const struct v4l2_file_operations venc_fops = {
+ .owner = THIS_MODULE,
+ .open = venc_open,
+ .release = venc_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = v4l2_compat_ioctl32,
+#endif
+};
+
+static int venc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct video_device *vdev;
+ struct venus_core *core;
+ int ret;
+
+ if (!dev->parent)
+ return -EPROBE_DEFER;
+
+ core = dev_get_drvdata(dev->parent);
+ if (!core)
+ return -EPROBE_DEFER;
+
+ if (core->res->hfi_version == HFI_VERSION_3XX) {
+ core->core1_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(core->core1_clk))
+ return PTR_ERR(core->core1_clk);
+ }
+
+ platform_set_drvdata(pdev, core);
+
+ vdev = video_device_alloc();
+ if (!vdev)
+ return -ENOMEM;
+
+ vdev->release = video_device_release;
+ vdev->fops = &venc_fops;
+ vdev->ioctl_ops = &venc_ioctl_ops;
+ vdev->vfl_dir = VFL_DIR_M2M;
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vdev_release;
+
+ core->vdev_enc = vdev;
+ core->dev_enc = dev;
+
+ video_set_drvdata(vdev, core);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+err_vdev_release:
+ video_device_release(vdev);
+ return ret;
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+ struct venus_core *core = dev_get_drvdata(pdev->dev.parent);
+
+ video_unregister_device(core->vdev_enc);
+ pm_runtime_disable(core->dev_enc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int venc_runtime_suspend(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+
+ if (core->res->hfi_version == HFI_VERSION_1XX)
+ return 0;
+
+ writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+ clk_disable_unprepare(core->core1_clk);
+ writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+
+ return 0;
+}
+
+static int venc_runtime_resume(struct device *dev)
+{
+ struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ if (core->res->hfi_version == HFI_VERSION_1XX)
+ return 0;
+
+ writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+ ret = clk_prepare_enable(core->core1_clk);
+ writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops venc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(venc_runtime_suspend, venc_runtime_resume, NULL)
+};
+
+static const struct of_device_id venc_dt_match[] = {
+ { .compatible = "venus-encoder" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, venc_dt_match);
+
+static struct platform_driver qcom_venus_enc_driver = {
+ .probe = venc_probe,
+ .remove = venc_remove,
+ .driver = {
+ .name = "qcom-venus-encoder",
+ .of_match_table = venc_dt_match,
+ .pm = &venc_pm_ops,
+ },
+};
+module_platform_driver(qcom_venus_enc_driver);
+
+MODULE_ALIAS("platform:qcom-venus-encoder");
+MODULE_DESCRIPTION("Qualcomm Venus video encoder driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/venus/venc.h b/drivers/media/platform/qcom/venus/venc.h
new file mode 100644
index 000000000000..9daca669f307
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/venc.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __VENUS_VENC_H__
+#define __VENUS_VENC_H__
+
+struct venus_inst;
+
+int venc_ctrl_init(struct venus_inst *inst);
+void venc_ctrl_deinit(struct venus_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
new file mode 100644
index 000000000000..ab0fe51ff0f7
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+
+#include "core.h"
+#include "venc.h"
+
+#define BITRATE_MIN 32000
+#define BITRATE_MAX 160000000
+#define BITRATE_DEFAULT 1000000
+#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2)
+#define BITRATE_STEP 100
+#define SLICE_BYTE_SIZE_MAX 1024
+#define SLICE_BYTE_SIZE_MIN 1024
+#define SLICE_MB_SIZE_MAX 300
+#define INTRA_REFRESH_MBS_MAX 300
+#define AT_SLICE_BOUNDARY \
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+
+static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct venus_inst *inst = ctrl_to_inst(ctrl);
+ struct venc_controls *ctr = &inst->controls.enc;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctr->bitrate_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctr->bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctr->bitrate_peak = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ ctr->h264_entropy_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ ctr->profile.mpeg4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ ctr->profile.h264 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ ctr->profile.vpx = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ ctr->level.mpeg4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ ctr->level.h264 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ ctr->h264_i_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ ctr->h264_p_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ ctr->h264_b_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ ctr->h264_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ ctr->h264_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ ctr->multi_slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ ctr->multi_slice_max_bytes = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ ctr->multi_slice_max_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ ctr->h264_loop_filter_alpha = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ ctr->h264_loop_filter_beta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ ctr->h264_loop_filter_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ ctr->header_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctr->gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+ ctr->h264_i_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP:
+ ctr->vp8_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP:
+ ctr->vp8_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ ctr->num_b_frames = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops venc_ctrl_ops = {
+ .s_ctrl = venc_op_s_ctrl,
+};
+
+int venc_ctrl_init(struct venus_inst *inst)
+{
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 27);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+ ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
+ (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)),
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ 0, V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY,
+ ~((1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE) |
+ (1 << V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE)),
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+ 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH)),
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ 0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ AT_SLICE_BOUNDARY,
+ 0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+ 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT_PEAK);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN,
+ SLICE_BYTE_SIZE_MAX, 1, SLICE_BYTE_SIZE_MIN);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1,
+ SLICE_MB_SIZE_MAX, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, -6, 6, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, -6, 6, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+ 0, INTRA_REFRESH_MBS_MAX, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 12);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, 1, 128, 1, 1);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, 1, 128, 1, 128);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, 0, (1 << 16) - 1, 1, 0);
+
+ ret = inst->ctrl_handler.error;
+ if (ret)
+ goto err;
+
+ ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+}
+
+void venc_ctrl_deinit(struct venus_inst *inst)
+{
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+}
diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c
index 7146fc5ef168..2988031d285d 100644
--- a/drivers/media/platform/rcar-fcp.c
+++ b/drivers/media/platform/rcar-fcp.c
@@ -53,14 +53,7 @@ struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
if (fcp->dev->of_node != np)
continue;
- /*
- * Make sure the module won't be unloaded behind our back. This
- * is a poor man's safety net, the module should really not be
- * unloaded while FCP users can be active.
- */
- if (!try_module_get(fcp->dev->driver->owner))
- fcp = NULL;
-
+ get_device(fcp->dev);
goto done;
}
@@ -81,10 +74,16 @@ EXPORT_SYMBOL_GPL(rcar_fcp_get);
void rcar_fcp_put(struct rcar_fcp_device *fcp)
{
if (fcp)
- module_put(fcp->dev->driver->owner);
+ put_device(fcp->dev);
}
EXPORT_SYMBOL_GPL(rcar_fcp_put);
+struct device *rcar_fcp_get_device(struct rcar_fcp_device *fcp)
+{
+ return fcp->dev;
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_get_device);
+
/**
* rcar_fcp_enable - Enable an FCP
* @fcp: The FCP instance
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
index 111d2a151f6a..af4c98b44d2e 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/rcar-vin/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_RCAR_VIN
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
---help---
Support for Renesas R-Car Video Input (VIN) driver.
Supports R-Car Gen2 SoCs.
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 098a0b1cc10a..77dff047c41c 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -21,7 +21,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include "rcar-vin.h"
@@ -31,6 +31,20 @@
#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
+static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
+{
+ unsigned int pad;
+
+ if (sd->entity.num_pads <= 1)
+ return 0;
+
+ for (pad = 0; pad < sd->entity.num_pads; pad++)
+ if (sd->entity.pads[pad].flags & direction)
+ return pad;
+
+ return -EINVAL;
+}
+
static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
{
struct v4l2_subdev *sd = entity->subdev;
@@ -39,6 +53,7 @@ static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
};
code.index = 0;
+ code.pad = entity->source_pad;
while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
code.index++;
switch (code.code) {
@@ -86,14 +101,9 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
{
struct rvin_dev *vin = notifier_to_vin(notifier);
- if (vin->digital.subdev == subdev) {
- vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
- rvin_v4l2_remove(vin);
- vin->digital.subdev = NULL;
- return;
- }
-
- vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name);
+ vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
+ rvin_v4l2_remove(vin);
+ vin->digital.subdev = NULL;
}
static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
@@ -101,27 +111,37 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_subdev *asd)
{
struct rvin_dev *vin = notifier_to_vin(notifier);
+ int ret;
v4l2_set_subdev_hostdata(subdev, vin);
- if (vin->digital.asd.match.of.node == subdev->dev->of_node) {
- vin_dbg(vin, "bound digital subdev %s\n", subdev->name);
- vin->digital.subdev = subdev;
- return 0;
- }
+ /* Find source and sink pad of remote subdevice */
- vin_err(vin, "no entity for subdev %s to bind\n", subdev->name);
- return -EINVAL;
+ ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
+ if (ret < 0)
+ return ret;
+ vin->digital.source_pad = ret;
+
+ ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
+ vin->digital.sink_pad = ret < 0 ? 0 : ret;
+
+ vin->digital.subdev = subdev;
+
+ vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
+ subdev->name, vin->digital.source_pad,
+ vin->digital.sink_pad);
+
+ return 0;
}
static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
struct device_node *ep,
struct v4l2_mbus_config *mbus_cfg)
{
- struct v4l2_of_endpoint v4l2_ep;
+ struct v4l2_fwnode_endpoint v4l2_ep;
int ret;
- ret = v4l2_of_parse_endpoint(ep, &v4l2_ep);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
if (ret) {
vin_err(vin, "Could not parse v4l2 endpoint\n");
return -EINVAL;
@@ -151,7 +171,7 @@ static int rvin_digital_graph_parse(struct rvin_dev *vin)
struct device_node *ep, *np;
int ret;
- vin->digital.asd.match.of.node = NULL;
+ vin->digital.asd.match.fwnode.fwnode = NULL;
vin->digital.subdev = NULL;
/*
@@ -175,8 +195,8 @@ static int rvin_digital_graph_parse(struct rvin_dev *vin)
if (ret)
return ret;
- vin->digital.asd.match.of.node = np;
- vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF;
+ vin->digital.asd.match.fwnode.fwnode = of_fwnode_handle(np);
+ vin->digital.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
return 0;
}
@@ -190,7 +210,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
if (ret)
return ret;
- if (!vin->digital.asd.match.of.node) {
+ if (!vin->digital.asd.match.fwnode.fwnode) {
vin_dbg(vin, "No digital subdevice found\n");
return -ENODEV;
}
@@ -203,7 +223,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
subdevs[0] = &vin->digital.asd;
vin_dbg(vin, "Found digital subdevice %s\n",
- of_node_full_name(subdevs[0]->match.of.node));
+ of_node_full_name(to_of_node(subdevs[0]->match.fwnode.fwnode)));
vin->notifier.num_subdevs = 1;
vin->notifier.subdevs = subdevs;
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 9ccd5ff55e19..b136844499f6 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -119,6 +119,15 @@
#define VNDMR2_FTEV (1 << 17)
#define VNDMR2_VLV(n) ((n & 0xf) << 12)
+struct rvin_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \
+ struct rvin_buffer, \
+ vb)->list)
+
static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset)
{
iowrite32(value, vin->base + offset);
@@ -269,48 +278,6 @@ static int rvin_setup(struct rvin_dev *vin)
return 0;
}
-static void rvin_capture_on(struct rvin_dev *vin)
-{
- vin_dbg(vin, "Capture on in %s mode\n",
- vin->continuous ? "continuous" : "single");
-
- if (vin->continuous)
- /* Continuous Frame Capture Mode */
- rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
- else
- /* Single Frame Capture Mode */
- rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
-}
-
-static void rvin_capture_off(struct rvin_dev *vin)
-{
- /* Set continuous & single transfer off */
- rvin_write(vin, 0, VNFC_REG);
-}
-
-static int rvin_capture_start(struct rvin_dev *vin)
-{
- int ret;
-
- rvin_crop_scale_comp(vin);
-
- ret = rvin_setup(vin);
- if (ret)
- return ret;
-
- rvin_capture_on(vin);
-
- return 0;
-}
-
-static void rvin_capture_stop(struct rvin_dev *vin)
-{
- rvin_capture_off(vin);
-
- /* Disable module */
- rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
-}
-
static void rvin_disable_interrupts(struct rvin_dev *vin)
{
rvin_write(vin, 0, VNIE_REG);
@@ -377,6 +344,99 @@ static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
rvin_write(vin, offset, VNMB_REG(slot));
}
+/* Moves a buffer from the queue to the HW slots */
+static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+{
+ struct rvin_buffer *buf;
+ struct vb2_v4l2_buffer *vbuf;
+ dma_addr_t phys_addr_top;
+
+ if (vin->queue_buf[slot] != NULL)
+ return true;
+
+ if (list_empty(&vin->buf_list))
+ return false;
+
+ vin_dbg(vin, "Filling HW slot: %d\n", slot);
+
+ /* Keep track of buffer we give to HW */
+ buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
+ vbuf = &buf->vb;
+ list_del_init(to_buf_list(vbuf));
+ vin->queue_buf[slot] = vbuf;
+
+ /* Setup DMA */
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+ rvin_set_slot_addr(vin, slot, phys_addr_top);
+
+ return true;
+}
+
+static bool rvin_fill_hw(struct rvin_dev *vin)
+{
+ int slot, limit;
+
+ limit = vin->continuous ? HW_BUFFER_NUM : 1;
+
+ for (slot = 0; slot < limit; slot++)
+ if (!rvin_fill_hw_slot(vin, slot))
+ return false;
+ return true;
+}
+
+static void rvin_capture_on(struct rvin_dev *vin)
+{
+ vin_dbg(vin, "Capture on in %s mode\n",
+ vin->continuous ? "continuous" : "single");
+
+ if (vin->continuous)
+ /* Continuous Frame Capture Mode */
+ rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
+ else
+ /* Single Frame Capture Mode */
+ rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
+}
+
+static int rvin_capture_start(struct rvin_dev *vin)
+{
+ struct rvin_buffer *buf, *node;
+ int bufs, ret;
+
+ /* Count number of free buffers */
+ bufs = 0;
+ list_for_each_entry_safe(buf, node, &vin->buf_list, list)
+ bufs++;
+
+ /* Continuous capture requires more buffers then there are HW slots */
+ vin->continuous = bufs > HW_BUFFER_NUM;
+
+ if (!rvin_fill_hw(vin)) {
+ vin_err(vin, "HW not ready to start, not enough buffers available\n");
+ return -EINVAL;
+ }
+
+ rvin_crop_scale_comp(vin);
+
+ ret = rvin_setup(vin);
+ if (ret)
+ return ret;
+
+ rvin_capture_on(vin);
+
+ vin->state = RUNNING;
+
+ return 0;
+}
+
+static void rvin_capture_stop(struct rvin_dev *vin)
+{
+ /* Set continuous & single transfer off */
+ rvin_write(vin, 0, VNFC_REG);
+
+ /* Disable module */
+ rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
+}
+
/* -----------------------------------------------------------------------------
* Crop and Scaling Gen2
*/
@@ -839,61 +899,12 @@ void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
#define RVIN_TIMEOUT_MS 100
#define RVIN_RETRIES 10
-struct rvin_buffer {
- struct vb2_v4l2_buffer vb;
- struct list_head list;
-};
-
-#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \
- struct rvin_buffer, \
- vb)->list)
-
-/* Moves a buffer from the queue to the HW slots */
-static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
-{
- struct rvin_buffer *buf;
- struct vb2_v4l2_buffer *vbuf;
- dma_addr_t phys_addr_top;
-
- if (vin->queue_buf[slot] != NULL)
- return true;
-
- if (list_empty(&vin->buf_list))
- return false;
-
- vin_dbg(vin, "Filling HW slot: %d\n", slot);
-
- /* Keep track of buffer we give to HW */
- buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
- vbuf = &buf->vb;
- list_del_init(to_buf_list(vbuf));
- vin->queue_buf[slot] = vbuf;
-
- /* Setup DMA */
- phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
- rvin_set_slot_addr(vin, slot, phys_addr_top);
-
- return true;
-}
-
-static bool rvin_fill_hw(struct rvin_dev *vin)
-{
- int slot, limit;
-
- limit = vin->continuous ? HW_BUFFER_NUM : 1;
-
- for (slot = 0; slot < limit; slot++)
- if (!rvin_fill_hw_slot(vin, slot))
- return false;
- return true;
-}
-
static irqreturn_t rvin_irq(int irq, void *data)
{
struct rvin_dev *vin = data;
u32 int_status, vnms;
int slot;
- unsigned int sequence, handled = 0;
+ unsigned int i, sequence, handled = 0;
unsigned long flags;
spin_lock_irqsave(&vin->qlock, flags);
@@ -955,8 +966,20 @@ static irqreturn_t rvin_irq(int irq, void *data)
* the VnMBm registers.
*/
if (vin->continuous) {
- rvin_capture_off(vin);
+ rvin_capture_stop(vin);
vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence);
+
+ /* Maybe we can continue in single capture mode */
+ for (i = 0; i < HW_BUFFER_NUM; i++) {
+ if (vin->queue_buf[i]) {
+ list_add(to_buf_list(vin->queue_buf[i]),
+ &vin->buf_list);
+ vin->queue_buf[i] = NULL;
+ }
+ }
+
+ if (!list_empty(&vin->buf_list))
+ rvin_capture_start(vin);
}
} else {
/*
@@ -1041,8 +1064,7 @@ static void rvin_buffer_queue(struct vb2_buffer *vb)
* capturing if HW is ready to continue.
*/
if (vin->state == STALLED)
- if (rvin_fill_hw(vin))
- rvin_capture_on(vin);
+ rvin_capture_start(vin);
spin_unlock_irqrestore(&vin->qlock, flags);
}
@@ -1059,25 +1081,9 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
spin_lock_irqsave(&vin->qlock, flags);
- vin->state = RUNNING;
vin->sequence = 0;
- /* Continuous capture requires more buffers then there are HW slots */
- vin->continuous = count > HW_BUFFER_NUM;
-
- /*
- * This should never happen but if we don't have enough
- * buffers for HW bail out
- */
- if (!rvin_fill_hw(vin)) {
- vin_err(vin, "HW not ready to start, not enough buffers available\n");
- ret = -EINVAL;
- goto out;
- }
-
ret = rvin_capture_start(vin);
-out:
- /* Return all buffers if something went wrong */
if (ret) {
return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
v4l2_subdev_call(sd, video, s_stream, 0);
@@ -1183,7 +1189,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
q->ops = &rvin_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 2;
+ q->min_buffers_needed = 1;
q->dev = vin->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index 2bbe6d495fa6..dd37ea811680 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -111,7 +111,7 @@ static int rvin_reset_format(struct rvin_dev *vin)
struct v4l2_mbus_framefmt *mf = &fmt.format;
int ret;
- fmt.pad = vin->src_pad_idx;
+ fmt.pad = vin->digital.source_pad;
ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
if (ret)
@@ -151,6 +151,9 @@ static int rvin_reset_format(struct rvin_dev *vin)
rvin_reset_crop_compose(vin);
+ vin->format.bytesperline = rvin_format_bytesperline(&vin->format);
+ vin->format.sizeimage = rvin_format_sizeimage(&vin->format);
+
return 0;
}
@@ -175,7 +178,7 @@ static int __rvin_try_format_source(struct rvin_dev *vin,
if (pad_cfg == NULL)
return -ENOMEM;
- format.pad = vin->src_pad_idx;
+ format.pad = vin->digital.source_pad;
field = pix->field;
@@ -203,8 +206,8 @@ static int __rvin_try_format(struct rvin_dev *vin,
struct v4l2_pix_format *pix,
struct rvin_source_fmt *source)
{
- const struct rvin_video_format *info;
u32 rwidth, rheight, walign;
+ int ret;
/* Requested */
rwidth = pix->width;
@@ -214,17 +217,11 @@ static int __rvin_try_format(struct rvin_dev *vin,
if (pix->field == V4L2_FIELD_ANY)
pix->field = vin->format.field;
- /*
- * Retrieve format information and select the current format if the
- * requested format isn't supported.
- */
- info = rvin_format_from_pixel(pix->pixelformat);
- if (!info) {
- vin_dbg(vin, "Format %x not found, keeping %x\n",
- pix->pixelformat, vin->format.pixelformat);
- *pix = vin->format;
- pix->width = rwidth;
- pix->height = rheight;
+ /* If requested format is not supported fallback to the default */
+ if (!rvin_format_from_pixel(pix->pixelformat)) {
+ vin_dbg(vin, "Format 0x%x not found, using default 0x%x\n",
+ pix->pixelformat, RVIN_DEFAULT_FORMAT);
+ pix->pixelformat = RVIN_DEFAULT_FORMAT;
}
/* Always recalculate */
@@ -232,7 +229,9 @@ static int __rvin_try_format(struct rvin_dev *vin,
pix->sizeimage = 0;
/* Limit to source capabilities */
- __rvin_try_format_source(vin, which, pix, source);
+ ret = __rvin_try_format_source(vin, which, pix, source);
+ if (ret)
+ return ret;
switch (pix->field) {
case V4L2_FIELD_TOP:
@@ -480,10 +479,14 @@ static int rvin_enum_input(struct file *file, void *priv,
return ret;
i->type = V4L2_INPUT_TYPE_CAMERA;
- i->std = vin->vdev.tvnorms;
- if (v4l2_subdev_has_op(sd, pad, dv_timings_cap))
+ if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) {
i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ i->std = 0;
+ } else {
+ i->capabilities = V4L2_IN_CAP_STD;
+ i->std = vin->vdev.tvnorms;
+ }
strlcpy(i->name, "Camera", sizeof(i->name));
@@ -547,14 +550,16 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- int pad, ret;
+ int ret;
+
+ if (timings->pad)
+ return -EINVAL;
- pad = timings->pad;
- timings->pad = vin->sink_pad_idx;
+ timings->pad = vin->digital.sink_pad;
ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
- timings->pad = pad;
+ timings->pad = 0;
return ret;
}
@@ -570,12 +575,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh,
if (ret)
return ret;
- vin->source.width = timings->bt.width;
- vin->source.height = timings->bt.height;
- vin->format.width = timings->bt.width;
- vin->format.height = timings->bt.height;
-
- return 0;
+ /* Changing the timings will change the width/height */
+ return rvin_reset_format(vin);
}
static int rvin_g_dv_timings(struct file *file, void *priv_fh,
@@ -601,14 +602,16 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- int pad, ret;
+ int ret;
+
+ if (cap->pad)
+ return -EINVAL;
- pad = cap->pad;
- cap->pad = vin->sink_pad_idx;
+ cap->pad = vin->digital.sink_pad;
ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
- cap->pad = pad;
+ cap->pad = 0;
return ret;
}
@@ -617,17 +620,16 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- int input, ret;
+ int ret;
if (edid->pad)
return -EINVAL;
- input = edid->pad;
- edid->pad = vin->sink_pad_idx;
+ edid->pad = vin->digital.sink_pad;
ret = v4l2_subdev_call(sd, pad, get_edid, edid);
- edid->pad = input;
+ edid->pad = 0;
return ret;
}
@@ -636,17 +638,16 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- int input, ret;
+ int ret;
if (edid->pad)
return -EINVAL;
- input = edid->pad;
- edid->pad = vin->sink_pad_idx;
+ edid->pad = vin->digital.sink_pad;
ret = v4l2_subdev_call(sd, pad, set_edid, edid);
- edid->pad = input;
+ edid->pad = 0;
return ret;
}
@@ -869,7 +870,7 @@ int rvin_v4l2_probe(struct rvin_dev *vin)
{
struct video_device *vdev = &vin->vdev;
struct v4l2_subdev *sd = vin_to_source(vin);
- int pad_idx, ret;
+ int ret;
v4l2_set_subdev_hostdata(sd, vin);
@@ -915,22 +916,6 @@ int rvin_v4l2_probe(struct rvin_dev *vin)
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
- vin->src_pad_idx = 0;
- for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
- if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE)
- break;
- if (pad_idx >= sd->entity.num_pads)
- return -EINVAL;
-
- vin->src_pad_idx = pad_idx;
-
- vin->sink_pad_idx = 0;
- for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
- if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) {
- vin->sink_pad_idx = pad_idx;
- break;
- }
-
vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
rvin_reset_format(vin);
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 727e215c0871..9bfb5a7c4dc4 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -74,6 +74,8 @@ struct rvin_video_format {
* @subdev: subdevice matched using async framework
* @code: Media bus format from source
* @mbus_cfg: Media bus format from DT
+ * @source_pad: source pad of remote subdevice
+ * @sink_pad: sink pad of remote subdevice
*/
struct rvin_graph_entity {
struct v4l2_async_subdev asd;
@@ -81,6 +83,9 @@ struct rvin_graph_entity {
u32 code;
struct v4l2_mbus_config mbus_cfg;
+
+ unsigned int source_pad;
+ unsigned int sink_pad;
};
/**
@@ -91,8 +96,6 @@ struct rvin_graph_entity {
*
* @vdev: V4L2 video device associated with VIN
* @v4l2_dev: V4L2 device
- * @src_pad_idx: source pad index for media controller drivers
- * @sink_pad_idx: sink pad index for media controller drivers
* @ctrl_handler: V4L2 control handler
* @notifier: V4L2 asynchronous subdevs notifier
* @digital: entity in the DT for local digital subdevice
@@ -121,8 +124,6 @@ struct rvin_dev {
struct video_device vdev;
struct v4l2_device v4l2_dev;
- int src_pad_idx;
- int sink_pad_idx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
struct rvin_graph_entity digital;
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
new file mode 100644
index 000000000000..522364ff0d5d
--- /dev/null
+++ b/drivers/media/platform/rcar_drif.c
@@ -0,0 +1,1498 @@
+/*
+ * R-Car Gen3 Digital Radio Interface (DRIF) driver
+ *
+ * Copyright (C) 2017 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The R-Car DRIF is a receive only MSIOF like controller with an
+ * external master device driving the SCK. It receives data into a FIFO,
+ * then this driver uses the SYS-DMAC engine to move the data from
+ * the device to memory.
+ *
+ * Each DRIF channel DRIFx (as per datasheet) contains two internal
+ * channels DRIFx0 & DRIFx1 within itself with each having its own resources
+ * like module clk, register set, irq and dma. These internal channels share
+ * common CLK & SYNC from master. The two data pins D0 & D1 shall be
+ * considered to represent the two internal channels. This internal split
+ * is not visible to the master device.
+ *
+ * Depending on the master device, a DRIF channel can use
+ * (1) both internal channels (D0 & D1) to receive data in parallel (or)
+ * (2) one internal channel (D0 or D1) to receive data
+ *
+ * The primary design goal of this controller is to act as a Digital Radio
+ * Interface that receives digital samples from a tuner device. Hence the
+ * driver exposes the device as a V4L2 SDR device. In order to qualify as
+ * a V4L2 SDR device, it should possess a tuner interface as mandated by the
+ * framework. This driver expects a tuner driver (sub-device) to bind
+ * asynchronously with this device and the combined drivers shall expose
+ * a V4L2 compliant SDR device. The DRIF driver is independent of the
+ * tuner vendor.
+ *
+ * The DRIF h/w can support I2S mode and Frame start synchronization pulse mode.
+ * This driver is tested for I2S mode only because of the availability of
+ * suitable master devices. Hence, not all configurable options of DRIF h/w
+ * like lsb/msb first, syncdl, dtdl etc. are exposed via DT and I2S defaults
+ * are used. These can be exposed later if needed after testing.
+ */
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/ioctl.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* DRIF register offsets */
+#define RCAR_DRIF_SITMDR1 0x00
+#define RCAR_DRIF_SITMDR2 0x04
+#define RCAR_DRIF_SITMDR3 0x08
+#define RCAR_DRIF_SIRMDR1 0x10
+#define RCAR_DRIF_SIRMDR2 0x14
+#define RCAR_DRIF_SIRMDR3 0x18
+#define RCAR_DRIF_SICTR 0x28
+#define RCAR_DRIF_SIFCTR 0x30
+#define RCAR_DRIF_SISTR 0x40
+#define RCAR_DRIF_SIIER 0x44
+#define RCAR_DRIF_SIRFDR 0x60
+
+#define RCAR_DRIF_RFOVF BIT(3) /* Receive FIFO overflow */
+#define RCAR_DRIF_RFUDF BIT(4) /* Receive FIFO underflow */
+#define RCAR_DRIF_RFSERR BIT(5) /* Receive frame sync error */
+#define RCAR_DRIF_REOF BIT(7) /* Frame reception end */
+#define RCAR_DRIF_RDREQ BIT(12) /* Receive data xfer req */
+#define RCAR_DRIF_RFFUL BIT(13) /* Receive FIFO full */
+
+/* SIRMDR1 */
+#define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME (0 << 28)
+#define RCAR_DRIF_SIRMDR1_SYNCMD_LR (3 << 28)
+
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH (0 << 25)
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW (1 << 25)
+
+#define RCAR_DRIF_SIRMDR1_MSB_FIRST (0 << 24)
+#define RCAR_DRIF_SIRMDR1_LSB_FIRST (1 << 24)
+
+#define RCAR_DRIF_SIRMDR1_DTDL_0 (0 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1 (1 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_2 (2 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_0PT5 (5 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1PT5 (6 << 20)
+
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0 (0 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1 (1 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_2 (2 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_3 (3 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5 (5 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5 (6 << 20)
+
+#define RCAR_DRIF_MDR_GRPCNT(n) (((n) - 1) << 30)
+#define RCAR_DRIF_MDR_BITLEN(n) (((n) - 1) << 24)
+#define RCAR_DRIF_MDR_WDCNT(n) (((n) - 1) << 16)
+
+/* Hidden Transmit register that controls CLK & SYNC */
+#define RCAR_DRIF_SITMDR1_PCON BIT(30)
+
+#define RCAR_DRIF_SICTR_RX_RISING_EDGE BIT(26)
+#define RCAR_DRIF_SICTR_RX_EN BIT(8)
+#define RCAR_DRIF_SICTR_RESET BIT(0)
+
+/* Constants */
+#define RCAR_DRIF_NUM_HWBUFS 32
+#define RCAR_DRIF_MAX_DEVS 4
+#define RCAR_DRIF_DEFAULT_NUM_HWBUFS 16
+#define RCAR_DRIF_DEFAULT_HWBUF_SIZE (4 * PAGE_SIZE)
+#define RCAR_DRIF_MAX_CHANNEL 2
+#define RCAR_SDR_BUFFER_SIZE SZ_64K
+
+/* Internal buffer status flags */
+#define RCAR_DRIF_BUF_DONE BIT(0) /* DMA completed */
+#define RCAR_DRIF_BUF_OVERFLOW BIT(1) /* Overflow detected */
+
+#define to_rcar_drif_buf_pair(sdr, ch_num, idx) \
+ (&((sdr)->ch[!(ch_num)]->buf[(idx)]))
+
+#define for_each_rcar_drif_channel(ch, ch_mask) \
+ for_each_set_bit(ch, ch_mask, RCAR_DRIF_MAX_CHANNEL)
+
+/* Debug */
+#define rdrif_dbg(sdr, fmt, arg...) \
+ dev_dbg(sdr->v4l2_dev.dev, fmt, ## arg)
+
+#define rdrif_err(sdr, fmt, arg...) \
+ dev_err(sdr->v4l2_dev.dev, fmt, ## arg)
+
+/* Stream formats */
+struct rcar_drif_format {
+ u32 pixelformat;
+ u32 buffersize;
+ u32 bitlen;
+ u32 wdcnt;
+ u32 num_ch;
+};
+
+/* Format descriptions for capture */
+static const struct rcar_drif_format formats[] = {
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU16BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .bitlen = 16,
+ .wdcnt = 1,
+ .num_ch = 2,
+ },
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU18BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .bitlen = 18,
+ .wdcnt = 1,
+ .num_ch = 2,
+ },
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU20BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .bitlen = 20,
+ .wdcnt = 1,
+ .num_ch = 2,
+ },
+};
+
+/* Buffer for a received frame from one or both internal channels */
+struct rcar_drif_frame_buf {
+ /* Common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+/* OF graph endpoint's V4L2 async data */
+struct rcar_drif_graph_ep {
+ struct v4l2_subdev *subdev; /* Async matched subdev */
+ struct v4l2_async_subdev asd; /* Async sub-device descriptor */
+};
+
+/* DMA buffer */
+struct rcar_drif_hwbuf {
+ void *addr; /* CPU-side address */
+ unsigned int status; /* Buffer status flags */
+};
+
+/* Internal channel */
+struct rcar_drif {
+ struct rcar_drif_sdr *sdr; /* Group device */
+ struct platform_device *pdev; /* Channel's pdev */
+ void __iomem *base; /* Base register address */
+ resource_size_t start; /* I/O resource offset */
+ struct dma_chan *dmach; /* Reserved DMA channel */
+ struct clk *clk; /* Module clock */
+ struct rcar_drif_hwbuf buf[RCAR_DRIF_NUM_HWBUFS]; /* H/W bufs */
+ dma_addr_t dma_handle; /* Handle for all bufs */
+ unsigned int num; /* Channel number */
+ bool acting_sdr; /* Channel acting as SDR device */
+};
+
+/* DRIF V4L2 SDR */
+struct rcar_drif_sdr {
+ struct device *dev; /* Platform device */
+ struct video_device *vdev; /* V4L2 SDR device */
+ struct v4l2_device v4l2_dev; /* V4L2 device */
+
+ /* Videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+ spinlock_t dma_lock; /* To serialize DMA cb of channels */
+
+ struct mutex v4l2_mutex; /* To serialize ioctls */
+ struct mutex vb_queue_mutex; /* To serialize streaming ioctls */
+ struct v4l2_ctrl_handler ctrl_hdl; /* SDR control handler */
+ struct v4l2_async_notifier notifier; /* For subdev (tuner) */
+ struct rcar_drif_graph_ep ep; /* Endpoint V4L2 async data */
+
+ /* Current V4L2 SDR format ptr */
+ const struct rcar_drif_format *fmt;
+
+ /* Device tree SYNC properties */
+ u32 mdr1;
+
+ /* Internals */
+ struct rcar_drif *ch[RCAR_DRIF_MAX_CHANNEL]; /* DRIFx0,1 */
+ unsigned long hw_ch_mask; /* Enabled channels per DT */
+ unsigned long cur_ch_mask; /* Used channels for an SDR FMT */
+ u32 num_hw_ch; /* Num of DT enabled channels */
+ u32 num_cur_ch; /* Num of used channels */
+ u32 hwbuf_size; /* Each DMA buffer size */
+ u32 produced; /* Buffers produced by sdr dev */
+};
+
+/* Register access functions */
+static void rcar_drif_write(struct rcar_drif *ch, u32 offset, u32 data)
+{
+ writel(data, ch->base + offset);
+}
+
+static u32 rcar_drif_read(struct rcar_drif *ch, u32 offset)
+{
+ return readl(ch->base + offset);
+}
+
+/* Release DMA channels */
+static void rcar_drif_release_dmachannels(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ if (sdr->ch[i]->dmach) {
+ dma_release_channel(sdr->ch[i]->dmach);
+ sdr->ch[i]->dmach = NULL;
+ }
+}
+
+/* Allocate DMA channels */
+static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr)
+{
+ struct dma_slave_config dma_cfg;
+ unsigned int i;
+ int ret = -ENODEV;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx");
+ if (!ch->dmach) {
+ rdrif_err(sdr, "ch%u: dma channel req failed\n", i);
+ goto dmach_error;
+ }
+
+ /* Configure slave */
+ memset(&dma_cfg, 0, sizeof(dma_cfg));
+ dma_cfg.src_addr = (phys_addr_t)(ch->start + RCAR_DRIF_SIRFDR);
+ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ret = dmaengine_slave_config(ch->dmach, &dma_cfg);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: dma slave config failed\n", i);
+ goto dmach_error;
+ }
+ }
+ return 0;
+
+dmach_error:
+ rcar_drif_release_dmachannels(sdr);
+ return ret;
+}
+
+/* Release queued vb2 buffers */
+static void rcar_drif_release_queued_bufs(struct rcar_drif_sdr *sdr,
+ enum vb2_buffer_state state)
+{
+ struct rcar_drif_frame_buf *fbuf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ list_for_each_entry_safe(fbuf, tmp, &sdr->queued_bufs, list) {
+ list_del(&fbuf->list);
+ vb2_buffer_done(&fbuf->vb.vb2_buf, state);
+ }
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Set MDR defaults */
+static inline void rcar_drif_set_mdr1(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ /* Set defaults for enabled internal channels */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ /* Refer MSIOF section in manual for this register setting */
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SITMDR1,
+ RCAR_DRIF_SITMDR1_PCON);
+
+ /* Setup MDR1 value */
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR1, sdr->mdr1);
+
+ rdrif_dbg(sdr, "ch%u: mdr1 = 0x%08x",
+ i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR1));
+ }
+}
+
+/* Set DRIF receive format */
+static int rcar_drif_set_format(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ rdrif_dbg(sdr, "setfmt: bitlen %u wdcnt %u num_ch %u\n",
+ sdr->fmt->bitlen, sdr->fmt->wdcnt, sdr->fmt->num_ch);
+
+ /* Sanity check */
+ if (sdr->fmt->num_ch > sdr->num_cur_ch) {
+ rdrif_err(sdr, "fmt num_ch %u cur_ch %u mismatch\n",
+ sdr->fmt->num_ch, sdr->num_cur_ch);
+ return -EINVAL;
+ }
+
+ /* Setup group, bitlen & wdcnt */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ u32 mdr;
+
+ /* Two groups */
+ mdr = RCAR_DRIF_MDR_GRPCNT(2) |
+ RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) |
+ RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt);
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR2, mdr);
+
+ mdr = RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) |
+ RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt);
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR3, mdr);
+
+ rdrif_dbg(sdr, "ch%u: new mdr[2,3] = 0x%08x, 0x%08x\n",
+ i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR2),
+ rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR3));
+ }
+ return 0;
+}
+
+/* Release DMA buffers */
+static void rcar_drif_release_buf(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ /* First entry contains the dma buf ptr */
+ if (ch->buf[0].addr) {
+ dma_free_coherent(&ch->pdev->dev,
+ sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
+ ch->buf[0].addr, ch->dma_handle);
+ ch->buf[0].addr = NULL;
+ }
+ }
+}
+
+/* Request DMA buffers */
+static int rcar_drif_request_buf(struct rcar_drif_sdr *sdr)
+{
+ int ret = -ENOMEM;
+ unsigned int i, j;
+ void *addr;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ /* Allocate DMA buffers */
+ addr = dma_alloc_coherent(&ch->pdev->dev,
+ sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
+ &ch->dma_handle, GFP_KERNEL);
+ if (!addr) {
+ rdrif_err(sdr,
+ "ch%u: dma alloc failed. num hwbufs %u size %u\n",
+ i, RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size);
+ goto error;
+ }
+
+ /* Split the chunk and populate bufctxt */
+ for (j = 0; j < RCAR_DRIF_NUM_HWBUFS; j++) {
+ ch->buf[j].addr = addr + (j * sdr->hwbuf_size);
+ ch->buf[j].status = 0;
+ }
+ }
+ return 0;
+error:
+ return ret;
+}
+
+/* Setup vb_queue minimum buffer requirements */
+static int rcar_drif_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+
+ /* Need at least 16 buffers */
+ if (vq->num_buffers + *num_buffers < 16)
+ *num_buffers = 16 - vq->num_buffers;
+
+ *num_planes = 1;
+ sizes[0] = PAGE_ALIGN(sdr->fmt->buffersize);
+ rdrif_dbg(sdr, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]);
+
+ return 0;
+}
+
+/* Enqueue buffer */
+static void rcar_drif_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_drif_frame_buf *fbuf =
+ container_of(vbuf, struct rcar_drif_frame_buf, vb);
+ unsigned long flags;
+
+ rdrif_dbg(sdr, "buf_queue idx %u\n", vb->index);
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ list_add_tail(&fbuf->list, &sdr->queued_bufs);
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Get a frame buf from list */
+static struct rcar_drif_frame_buf *
+rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr)
+{
+ struct rcar_drif_frame_buf *fbuf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ fbuf = list_first_entry_or_null(&sdr->queued_bufs, struct
+ rcar_drif_frame_buf, list);
+ if (!fbuf) {
+ /*
+ * App is late in enqueing buffers. Samples lost & there will
+ * be a gap in sequence number when app recovers
+ */
+ rdrif_dbg(sdr, "\napp late: prod %u\n", sdr->produced);
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+ return NULL;
+ }
+ list_del(&fbuf->list);
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+
+ return fbuf;
+}
+
+/* Helpers to set/clear buf pair status */
+static inline bool rcar_drif_bufs_done(struct rcar_drif_hwbuf **buf)
+{
+ return (buf[0]->status & buf[1]->status & RCAR_DRIF_BUF_DONE);
+}
+
+static inline bool rcar_drif_bufs_overflow(struct rcar_drif_hwbuf **buf)
+{
+ return ((buf[0]->status | buf[1]->status) & RCAR_DRIF_BUF_OVERFLOW);
+}
+
+static inline void rcar_drif_bufs_clear(struct rcar_drif_hwbuf **buf,
+ unsigned int bit)
+{
+ unsigned int i;
+
+ for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++)
+ buf[i]->status &= ~bit;
+}
+
+/* Channel DMA complete */
+static void rcar_drif_channel_complete(struct rcar_drif *ch, u32 idx)
+{
+ u32 str;
+
+ ch->buf[idx].status |= RCAR_DRIF_BUF_DONE;
+
+ /* Check for DRIF errors */
+ str = rcar_drif_read(ch, RCAR_DRIF_SISTR);
+ if (unlikely(str & RCAR_DRIF_RFOVF)) {
+ /* Writing the same clears it */
+ rcar_drif_write(ch, RCAR_DRIF_SISTR, str);
+
+ /* Overflow: some samples are lost */
+ ch->buf[idx].status |= RCAR_DRIF_BUF_OVERFLOW;
+ }
+}
+
+/* DMA callback for each stage */
+static void rcar_drif_dma_complete(void *dma_async_param)
+{
+ struct rcar_drif *ch = dma_async_param;
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_CHANNEL];
+ struct rcar_drif_frame_buf *fbuf;
+ bool overflow = false;
+ u32 idx, produced;
+ unsigned int i;
+
+ spin_lock(&sdr->dma_lock);
+
+ /* DMA can be terminated while the callback was waiting on lock */
+ if (!vb2_is_streaming(&sdr->vb_queue)) {
+ spin_unlock(&sdr->dma_lock);
+ return;
+ }
+
+ idx = sdr->produced % RCAR_DRIF_NUM_HWBUFS;
+ rcar_drif_channel_complete(ch, idx);
+
+ if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) {
+ buf[0] = ch->num ? to_rcar_drif_buf_pair(sdr, ch->num, idx) :
+ &ch->buf[idx];
+ buf[1] = ch->num ? &ch->buf[idx] :
+ to_rcar_drif_buf_pair(sdr, ch->num, idx);
+
+ /* Check if both DMA buffers are done */
+ if (!rcar_drif_bufs_done(buf)) {
+ spin_unlock(&sdr->dma_lock);
+ return;
+ }
+
+ /* Clear buf done status */
+ rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_DONE);
+
+ if (rcar_drif_bufs_overflow(buf)) {
+ overflow = true;
+ /* Clear the flag in status */
+ rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_OVERFLOW);
+ }
+ } else {
+ buf[0] = &ch->buf[idx];
+ if (buf[0]->status & RCAR_DRIF_BUF_OVERFLOW) {
+ overflow = true;
+ /* Clear the flag in status */
+ buf[0]->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+ }
+ }
+
+ /* Buffer produced for consumption */
+ produced = sdr->produced++;
+ spin_unlock(&sdr->dma_lock);
+
+ rdrif_dbg(sdr, "ch%u: prod %u\n", ch->num, produced);
+
+ /* Get fbuf */
+ fbuf = rcar_drif_get_fbuf(sdr);
+ if (!fbuf)
+ return;
+
+ for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++)
+ memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) +
+ i * sdr->hwbuf_size, buf[i]->addr, sdr->hwbuf_size);
+
+ fbuf->vb.field = V4L2_FIELD_NONE;
+ fbuf->vb.sequence = produced;
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, sdr->fmt->buffersize);
+
+ /* Set error state on overflow */
+ vb2_buffer_done(&fbuf->vb.vb2_buf,
+ overflow ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+}
+
+static int rcar_drif_qbuf(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ dma_addr_t addr = ch->dma_handle;
+ struct dma_async_tx_descriptor *rxd;
+ dma_cookie_t cookie;
+ int ret = -EIO;
+
+ /* Setup cyclic DMA with given buffers */
+ rxd = dmaengine_prep_dma_cyclic(ch->dmach, addr,
+ sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
+ sdr->hwbuf_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rxd) {
+ rdrif_err(sdr, "ch%u: prep dma cyclic failed\n", ch->num);
+ return ret;
+ }
+
+ /* Submit descriptor */
+ rxd->callback = rcar_drif_dma_complete;
+ rxd->callback_param = ch;
+ cookie = dmaengine_submit(rxd);
+ if (dma_submit_error(cookie)) {
+ rdrif_err(sdr, "ch%u: dma submit failed\n", ch->num);
+ return ret;
+ }
+
+ dma_async_issue_pending(ch->dmach);
+ return 0;
+}
+
+/* Enable reception */
+static int rcar_drif_enable_rx(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+ u32 ctr;
+ int ret;
+
+ /*
+ * When both internal channels are enabled, they can be synchronized
+ * only by the master
+ */
+
+ /* Enable receive */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR);
+ ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
+ RCAR_DRIF_SICTR_RX_EN);
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr);
+ }
+
+ /* Check receive enabled */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+ ctr, ctr & RCAR_DRIF_SICTR_RX_EN, 7, 100000);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: rx en failed. ctr 0x%08x\n", i,
+ rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR));
+ break;
+ }
+ }
+ return ret;
+}
+
+/* Disable reception */
+static void rcar_drif_disable_rx(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+ u32 ctr;
+ int ret;
+
+ /* Disable receive */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR);
+ ctr &= ~RCAR_DRIF_SICTR_RX_EN;
+ rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr);
+ }
+
+ /* Check receive disabled */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+ ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN), 7, 100000);
+ if (ret)
+ dev_warn(&sdr->vdev->dev,
+ "ch%u: failed to disable rx. ctr 0x%08x\n",
+ i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR));
+ }
+}
+
+/* Stop channel */
+static void rcar_drif_stop_channel(struct rcar_drif *ch)
+{
+ /* Disable DMA receive interrupt */
+ rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00000000);
+
+ /* Terminate all DMA transfers */
+ dmaengine_terminate_sync(ch->dmach);
+}
+
+/* Stop receive operation */
+static void rcar_drif_stop(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ /* Disable Rx */
+ rcar_drif_disable_rx(sdr);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ rcar_drif_stop_channel(sdr->ch[i]);
+}
+
+/* Start channel */
+static int rcar_drif_start_channel(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ u32 ctr, str;
+ int ret;
+
+ /* Reset receive */
+ rcar_drif_write(ch, RCAR_DRIF_SICTR, RCAR_DRIF_SICTR_RESET);
+ ret = readl_poll_timeout(ch->base + RCAR_DRIF_SICTR, ctr,
+ !(ctr & RCAR_DRIF_SICTR_RESET), 7, 100000);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: failed to reset rx. ctr 0x%08x\n",
+ ch->num, rcar_drif_read(ch, RCAR_DRIF_SICTR));
+ return ret;
+ }
+
+ /* Queue buffers for DMA */
+ ret = rcar_drif_qbuf(ch);
+ if (ret)
+ return ret;
+
+ /* Clear status register flags */
+ str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
+ RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
+ rcar_drif_write(ch, RCAR_DRIF_SISTR, str);
+
+ /* Enable DMA receive interrupt */
+ rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00009000);
+
+ return ret;
+}
+
+/* Start receive operation */
+static int rcar_drif_start(struct rcar_drif_sdr *sdr)
+{
+ unsigned long enabled = 0;
+ unsigned int i;
+ int ret;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = rcar_drif_start_channel(sdr->ch[i]);
+ if (ret)
+ goto start_error;
+ enabled |= BIT(i);
+ }
+
+ ret = rcar_drif_enable_rx(sdr);
+ if (ret)
+ goto enable_error;
+
+ sdr->produced = 0;
+ return ret;
+
+enable_error:
+ rcar_drif_disable_rx(sdr);
+start_error:
+ for_each_rcar_drif_channel(i, &enabled)
+ rcar_drif_stop_channel(sdr->ch[i]);
+
+ return ret;
+}
+
+/* Start streaming */
+static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+ unsigned long enabled = 0;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&sdr->v4l2_mutex);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = clk_prepare_enable(sdr->ch[i]->clk);
+ if (ret)
+ goto error;
+ enabled |= BIT(i);
+ }
+
+ /* Set default MDRx settings */
+ rcar_drif_set_mdr1(sdr);
+
+ /* Set new format */
+ ret = rcar_drif_set_format(sdr);
+ if (ret)
+ goto error;
+
+ if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL)
+ sdr->hwbuf_size = sdr->fmt->buffersize / RCAR_DRIF_MAX_CHANNEL;
+ else
+ sdr->hwbuf_size = sdr->fmt->buffersize;
+
+ rdrif_dbg(sdr, "num hwbufs %u, hwbuf_size %u\n",
+ RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size);
+
+ /* Alloc DMA channel */
+ ret = rcar_drif_alloc_dmachannels(sdr);
+ if (ret)
+ goto error;
+
+ /* Request buffers */
+ ret = rcar_drif_request_buf(sdr);
+ if (ret)
+ goto error;
+
+ /* Start Rx */
+ ret = rcar_drif_start(sdr);
+ if (ret)
+ goto error;
+
+ mutex_unlock(&sdr->v4l2_mutex);
+
+ return ret;
+
+error:
+ rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_QUEUED);
+ rcar_drif_release_buf(sdr);
+ rcar_drif_release_dmachannels(sdr);
+ for_each_rcar_drif_channel(i, &enabled)
+ clk_disable_unprepare(sdr->ch[i]->clk);
+
+ mutex_unlock(&sdr->v4l2_mutex);
+
+ return ret;
+}
+
+/* Stop streaming */
+static void rcar_drif_stop_streaming(struct vb2_queue *vq)
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+ unsigned int i;
+
+ mutex_lock(&sdr->v4l2_mutex);
+
+ /* Stop hardware streaming */
+ rcar_drif_stop(sdr);
+
+ /* Return all queued buffers to vb2 */
+ rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_ERROR);
+
+ /* Release buf */
+ rcar_drif_release_buf(sdr);
+
+ /* Release DMA channel resources */
+ rcar_drif_release_dmachannels(sdr);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ clk_disable_unprepare(sdr->ch[i]->clk);
+
+ mutex_unlock(&sdr->v4l2_mutex);
+}
+
+/* Vb2 ops */
+static const struct vb2_ops rcar_drif_vb2_ops = {
+ .queue_setup = rcar_drif_queue_setup,
+ .buf_queue = rcar_drif_buf_queue,
+ .start_streaming = rcar_drif_start_streaming,
+ .stop_streaming = rcar_drif_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int rcar_drif_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strlcpy(cap->card, sdr->vdev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ sdr->vdev->name);
+
+ return 0;
+}
+
+static int rcar_drif_set_default_format(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ /* Matching fmt based on required channels is set as default */
+ if (sdr->num_hw_ch == formats[i].num_ch) {
+ sdr->fmt = &formats[i];
+ sdr->cur_ch_mask = sdr->hw_ch_mask;
+ sdr->num_cur_ch = sdr->num_hw_ch;
+ dev_dbg(sdr->dev, "default fmt[%u]: mask %lu num %u\n",
+ i, sdr->cur_ch_mask, sdr->num_cur_ch);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].pixelformat;
+
+ return 0;
+}
+
+static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ f->fmt.sdr.pixelformat = sdr->fmt->pixelformat;
+ f->fmt.sdr.buffersize = sdr->fmt->buffersize;
+
+ return 0;
+}
+
+static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct vb2_queue *q = &sdr->vb_queue;
+ unsigned int i;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(formats))
+ i = 0; /* Set the 1st format as default on no match */
+
+ sdr->fmt = &formats[i];
+ f->fmt.sdr.pixelformat = sdr->fmt->pixelformat;
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+
+ /*
+ * If a format demands one channel only out of two
+ * enabled channels, pick the 0th channel.
+ */
+ if (formats[i].num_ch < sdr->num_hw_ch) {
+ sdr->cur_ch_mask = BIT(0);
+ sdr->num_cur_ch = formats[i].num_ch;
+ } else {
+ sdr->cur_ch_mask = sdr->hw_ch_mask;
+ sdr->num_cur_ch = sdr->num_hw_ch;
+ }
+
+ rdrif_dbg(sdr, "cur: idx %u mask %lu num %u\n",
+ i, sdr->cur_ch_mask, sdr->num_cur_ch);
+
+ return 0;
+}
+
+static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+ return 0;
+ }
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+ f->fmt.sdr.buffersize = formats[0].buffersize;
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+
+ return 0;
+}
+
+/* Tuner subdev ioctls */
+static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ return v4l2_subdev_call(sdr->ep.subdev, tuner, enum_freq_bands, band);
+}
+
+static int rcar_drif_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ return v4l2_subdev_call(sdr->ep.subdev, tuner, g_frequency, f);
+}
+
+static int rcar_drif_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ return v4l2_subdev_call(sdr->ep.subdev, tuner, s_frequency, f);
+}
+
+static int rcar_drif_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ return v4l2_subdev_call(sdr->ep.subdev, tuner, g_tuner, vt);
+}
+
+static int rcar_drif_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *vt)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ return v4l2_subdev_call(sdr->ep.subdev, tuner, s_tuner, vt);
+}
+
+static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
+ .vidioc_querycap = rcar_drif_querycap,
+
+ .vidioc_enum_fmt_sdr_cap = rcar_drif_enum_fmt_sdr_cap,
+ .vidioc_g_fmt_sdr_cap = rcar_drif_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = rcar_drif_s_fmt_sdr_cap,
+ .vidioc_try_fmt_sdr_cap = rcar_drif_try_fmt_sdr_cap,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_s_frequency = rcar_drif_s_frequency,
+ .vidioc_g_frequency = rcar_drif_g_frequency,
+ .vidioc_s_tuner = rcar_drif_s_tuner,
+ .vidioc_g_tuner = rcar_drif_g_tuner,
+ .vidioc_enum_freq_bands = rcar_drif_enum_freq_bands,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rcar_drif_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr)
+{
+ int ret;
+
+ /* Init video_device structure */
+ sdr->vdev = video_device_alloc();
+ if (!sdr->vdev)
+ return -ENOMEM;
+
+ snprintf(sdr->vdev->name, sizeof(sdr->vdev->name), "R-Car DRIF");
+ sdr->vdev->fops = &rcar_drif_fops;
+ sdr->vdev->ioctl_ops = &rcar_drif_ioctl_ops;
+ sdr->vdev->release = video_device_release;
+ sdr->vdev->lock = &sdr->v4l2_mutex;
+ sdr->vdev->queue = &sdr->vb_queue;
+ sdr->vdev->queue->lock = &sdr->vb_queue_mutex;
+ sdr->vdev->ctrl_handler = &sdr->ctrl_hdl;
+ sdr->vdev->v4l2_dev = &sdr->v4l2_dev;
+ sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ video_set_drvdata(sdr->vdev, sdr);
+
+ /* Register V4L2 SDR device */
+ ret = video_register_device(sdr->vdev, VFL_TYPE_SDR, -1);
+ if (ret) {
+ video_device_release(sdr->vdev);
+ sdr->vdev = NULL;
+ dev_err(sdr->dev, "failed video_register_device (%d)\n", ret);
+ }
+
+ return ret;
+}
+
+static void rcar_drif_sdr_unregister(struct rcar_drif_sdr *sdr)
+{
+ video_unregister_device(sdr->vdev);
+ sdr->vdev = NULL;
+}
+
+/* Sub-device bound callback */
+static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rcar_drif_sdr *sdr =
+ container_of(notifier, struct rcar_drif_sdr, notifier);
+
+ if (sdr->ep.asd.match.fwnode.fwnode !=
+ of_fwnode_handle(subdev->dev->of_node)) {
+ rdrif_err(sdr, "subdev %s cannot bind\n", subdev->name);
+ return -EINVAL;
+ }
+
+ v4l2_set_subdev_hostdata(subdev, sdr);
+ sdr->ep.subdev = subdev;
+ rdrif_dbg(sdr, "bound asd %s\n", subdev->name);
+
+ return 0;
+}
+
+/* Sub-device unbind callback */
+static void rcar_drif_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rcar_drif_sdr *sdr =
+ container_of(notifier, struct rcar_drif_sdr, notifier);
+
+ if (sdr->ep.subdev != subdev) {
+ rdrif_err(sdr, "subdev %s is not bound\n", subdev->name);
+ return;
+ }
+
+ /* Free ctrl handler if initialized */
+ v4l2_ctrl_handler_free(&sdr->ctrl_hdl);
+ sdr->v4l2_dev.ctrl_handler = NULL;
+ sdr->ep.subdev = NULL;
+
+ rcar_drif_sdr_unregister(sdr);
+ rdrif_dbg(sdr, "unbind asd %s\n", subdev->name);
+}
+
+/* Sub-device registered notification callback */
+static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rcar_drif_sdr *sdr =
+ container_of(notifier, struct rcar_drif_sdr, notifier);
+ int ret;
+
+ /*
+ * The subdev tested at this point uses 4 controls. Using 10 as a worst
+ * case scenario hint. When less controls are needed there will be some
+ * unused memory and when more controls are needed the framework uses
+ * hash to manage controls within this number.
+ */
+ ret = v4l2_ctrl_handler_init(&sdr->ctrl_hdl, 10);
+ if (ret)
+ return -ENOMEM;
+
+ sdr->v4l2_dev.ctrl_handler = &sdr->ctrl_hdl;
+ ret = v4l2_device_register_subdev_nodes(&sdr->v4l2_dev);
+ if (ret) {
+ rdrif_err(sdr, "failed: register subdev nodes ret %d\n", ret);
+ goto error;
+ }
+
+ ret = v4l2_ctrl_add_handler(&sdr->ctrl_hdl,
+ sdr->ep.subdev->ctrl_handler, NULL);
+ if (ret) {
+ rdrif_err(sdr, "failed: ctrl add hdlr ret %d\n", ret);
+ goto error;
+ }
+
+ ret = rcar_drif_sdr_register(sdr);
+ if (ret)
+ goto error;
+
+ return ret;
+
+error:
+ v4l2_ctrl_handler_free(&sdr->ctrl_hdl);
+
+ return ret;
+}
+
+/* Read endpoint properties */
+static void rcar_drif_get_ep_properties(struct rcar_drif_sdr *sdr,
+ struct fwnode_handle *fwnode)
+{
+ u32 val;
+
+ /* Set the I2S defaults for SIRMDR1*/
+ sdr->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR | RCAR_DRIF_SIRMDR1_MSB_FIRST |
+ RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
+
+ /* Parse sync polarity from endpoint */
+ if (!fwnode_property_read_u32(fwnode, "sync-active", &val))
+ sdr->mdr1 |= val ? RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH :
+ RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
+ else
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH; /* default */
+
+ dev_dbg(sdr->dev, "mdr1 0x%08x\n", sdr->mdr1);
+}
+
+/* Parse sub-devs (tuner) to find a matching device */
+static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr)
+{
+ struct v4l2_async_notifier *notifier = &sdr->notifier;
+ struct fwnode_handle *fwnode, *ep;
+
+ notifier->subdevs = devm_kzalloc(sdr->dev, sizeof(*notifier->subdevs),
+ GFP_KERNEL);
+ if (!notifier->subdevs)
+ return -ENOMEM;
+
+ ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(sdr->dev->of_node),
+ NULL);
+ if (!ep)
+ return 0;
+
+ notifier->subdevs[notifier->num_subdevs] = &sdr->ep.asd;
+ fwnode = fwnode_graph_get_remote_port_parent(ep);
+ if (!fwnode) {
+ dev_warn(sdr->dev, "bad remote port parent\n");
+ fwnode_handle_put(ep);
+ return -EINVAL;
+ }
+
+ sdr->ep.asd.match.fwnode.fwnode = fwnode;
+ sdr->ep.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ notifier->num_subdevs++;
+
+ /* Get the endpoint properties */
+ rcar_drif_get_ep_properties(sdr, ep);
+
+ fwnode_handle_put(fwnode);
+ fwnode_handle_put(ep);
+
+ return 0;
+}
+
+/* Check if the given device is the primary bond */
+static bool rcar_drif_primary_bond(struct platform_device *pdev)
+{
+ return of_property_read_bool(pdev->dev.of_node, "renesas,primary-bond");
+}
+
+/* Check if both devices of the bond are enabled */
+static struct device_node *rcar_drif_bond_enabled(struct platform_device *p)
+{
+ struct device_node *np;
+
+ np = of_parse_phandle(p->dev.of_node, "renesas,bonding", 0);
+ if (np && of_device_is_available(np))
+ return np;
+
+ return NULL;
+}
+
+/* Check if the bonded device is probed */
+static int rcar_drif_bond_available(struct rcar_drif_sdr *sdr,
+ struct device_node *np)
+{
+ struct platform_device *pdev;
+ struct rcar_drif *ch;
+ int ret = 0;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ dev_err(sdr->dev, "failed to get bonded device from node\n");
+ return -ENODEV;
+ }
+
+ device_lock(&pdev->dev);
+ ch = platform_get_drvdata(pdev);
+ if (ch) {
+ /* Update sdr data in the bonded device */
+ ch->sdr = sdr;
+
+ /* Update sdr with bonded device data */
+ sdr->ch[ch->num] = ch;
+ sdr->hw_ch_mask |= BIT(ch->num);
+ } else {
+ /* Defer */
+ dev_info(sdr->dev, "defer probe\n");
+ ret = -EPROBE_DEFER;
+ }
+ device_unlock(&pdev->dev);
+
+ put_device(&pdev->dev);
+
+ return ret;
+}
+
+/* V4L2 SDR device probe */
+static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr)
+{
+ int ret;
+
+ /* Validate any supported format for enabled channels */
+ ret = rcar_drif_set_default_format(sdr);
+ if (ret) {
+ dev_err(sdr->dev, "failed to set default format\n");
+ return ret;
+ }
+
+ /* Set defaults */
+ sdr->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
+
+ mutex_init(&sdr->v4l2_mutex);
+ mutex_init(&sdr->vb_queue_mutex);
+ spin_lock_init(&sdr->queued_bufs_lock);
+ spin_lock_init(&sdr->dma_lock);
+ INIT_LIST_HEAD(&sdr->queued_bufs);
+
+ /* Init videobuf2 queue structure */
+ sdr->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+ sdr->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
+ sdr->vb_queue.drv_priv = sdr;
+ sdr->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
+ sdr->vb_queue.ops = &rcar_drif_vb2_ops;
+ sdr->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ /* Init videobuf2 queue */
+ ret = vb2_queue_init(&sdr->vb_queue);
+ if (ret) {
+ dev_err(sdr->dev, "failed: vb2_queue_init ret %d\n", ret);
+ return ret;
+ }
+
+ /* Register the v4l2_device */
+ ret = v4l2_device_register(sdr->dev, &sdr->v4l2_dev);
+ if (ret) {
+ dev_err(sdr->dev, "failed: v4l2_device_register ret %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Parse subdevs after v4l2_device_register because if the subdev
+ * is already probed, bound and complete will be called immediately
+ */
+ ret = rcar_drif_parse_subdevs(sdr);
+ if (ret)
+ goto error;
+
+ sdr->notifier.bound = rcar_drif_notify_bound;
+ sdr->notifier.unbind = rcar_drif_notify_unbind;
+ sdr->notifier.complete = rcar_drif_notify_complete;
+
+ /* Register notifier */
+ ret = v4l2_async_notifier_register(&sdr->v4l2_dev, &sdr->notifier);
+ if (ret < 0) {
+ dev_err(sdr->dev, "failed: notifier register ret %d\n", ret);
+ goto error;
+ }
+
+ return ret;
+
+error:
+ v4l2_device_unregister(&sdr->v4l2_dev);
+
+ return ret;
+}
+
+/* V4L2 SDR device remove */
+static void rcar_drif_sdr_remove(struct rcar_drif_sdr *sdr)
+{
+ v4l2_async_notifier_unregister(&sdr->notifier);
+ v4l2_device_unregister(&sdr->v4l2_dev);
+}
+
+/* DRIF channel probe */
+static int rcar_drif_probe(struct platform_device *pdev)
+{
+ struct rcar_drif_sdr *sdr;
+ struct device_node *np;
+ struct rcar_drif *ch;
+ struct resource *res;
+ int ret;
+
+ /* Reserve memory for enabled channel */
+ ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
+
+ ch->pdev = pdev;
+
+ /* Module clock */
+ ch->clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(ch->clk)) {
+ ret = PTR_ERR(ch->clk);
+ dev_err(&pdev->dev, "clk get failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* Register map */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ch->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ch->base)) {
+ ret = PTR_ERR(ch->base);
+ dev_err(&pdev->dev, "ioremap failed (%d)\n", ret);
+ return ret;
+ }
+ ch->start = res->start;
+ platform_set_drvdata(pdev, ch);
+
+ /* Check if both channels of the bond are enabled */
+ np = rcar_drif_bond_enabled(pdev);
+ if (np) {
+ /* Check if current channel acting as primary-bond */
+ if (!rcar_drif_primary_bond(pdev)) {
+ ch->num = 1; /* Primary bond is channel 0 always */
+ of_node_put(np);
+ return 0;
+ }
+ }
+
+ /* Reserve memory for SDR structure */
+ sdr = devm_kzalloc(&pdev->dev, sizeof(*sdr), GFP_KERNEL);
+ if (!sdr) {
+ of_node_put(np);
+ return -ENOMEM;
+ }
+ ch->sdr = sdr;
+ sdr->dev = &pdev->dev;
+
+ /* Establish links between SDR and channel(s) */
+ sdr->ch[ch->num] = ch;
+ sdr->hw_ch_mask = BIT(ch->num);
+ if (np) {
+ /* Check if bonded device is ready */
+ ret = rcar_drif_bond_available(sdr, np);
+ of_node_put(np);
+ if (ret)
+ return ret;
+ }
+ sdr->num_hw_ch = hweight_long(sdr->hw_ch_mask);
+
+ return rcar_drif_sdr_probe(sdr);
+}
+
+/* DRIF channel remove */
+static int rcar_drif_remove(struct platform_device *pdev)
+{
+ struct rcar_drif *ch = platform_get_drvdata(pdev);
+ struct rcar_drif_sdr *sdr = ch->sdr;
+
+ /* Channel 0 will be the SDR instance */
+ if (ch->num)
+ return 0;
+
+ /* SDR instance */
+ rcar_drif_sdr_remove(sdr);
+
+ return 0;
+}
+
+/* FIXME: Implement suspend/resume support */
+static int __maybe_unused rcar_drif_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused rcar_drif_resume(struct device *dev)
+{
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
+ rcar_drif_resume);
+
+static const struct of_device_id rcar_drif_of_table[] = {
+ { .compatible = "renesas,rcar-gen3-drif" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
+
+#define RCAR_DRIF_DRV_NAME "rcar_drif"
+static struct platform_driver rcar_drif_driver = {
+ .driver = {
+ .name = RCAR_DRIF_DRV_NAME,
+ .of_match_table = of_match_ptr(rcar_drif_of_table),
+ .pm = &rcar_drif_pm_ops,
+ },
+ .probe = rcar_drif_probe,
+ .remove = rcar_drif_remove,
+};
+
+module_platform_driver(rcar_drif_driver);
+
+MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
+MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c
index 42f25d241edd..3ee51fc3bb50 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/rcar_fdp1.c
@@ -1,5 +1,5 @@
/*
- * Renesas RCar Fine Display Processor
+ * Renesas R-Car Fine Display Processor
*
* Video format converter and frame deinterlacer device.
*
@@ -258,8 +258,9 @@ MODULE_PARM_DESC(debug, "activate debug info");
/* Internal Data (HW Version) */
#define FD1_IP_INTDATA 0x0800
-#define FD1_IP_H3 0x02010101
+#define FD1_IP_H3_ES1 0x02010101
#define FD1_IP_M3W 0x02010202
+#define FD1_IP_H3 0x02010203
/* LUTs */
#define FD1_LUT_DIF_ADJ 0x1000
@@ -2359,12 +2360,15 @@ static int fdp1_probe(struct platform_device *pdev)
hw_version = fdp1_read(fdp1, FD1_IP_INTDATA);
switch (hw_version) {
- case FD1_IP_H3:
- dprintk(fdp1, "FDP1 Version R-Car H3\n");
+ case FD1_IP_H3_ES1:
+ dprintk(fdp1, "FDP1 Version R-Car H3 ES1\n");
break;
case FD1_IP_M3W:
dprintk(fdp1, "FDP1 Version R-Car M3-W\n");
break;
+ case FD1_IP_H3:
+ dprintk(fdp1, "FDP1 Version R-Car H3\n");
+ break;
default:
dev_err(fdp1->dev, "FDP1 Unidentifiable (0x%08x)\n",
hw_version);
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 1b30be72f4f9..25c7a7d42292 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -80,7 +80,7 @@ static int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp)
camif_hw_set_test_pattern(camif, camif->test_pattern);
if (variant->has_img_effect)
camif_hw_set_effect(camif, camif->colorfx,
- camif->colorfx_cb, camif->colorfx_cr);
+ camif->colorfx_cr, camif->colorfx_cb);
if (variant->ip_revision == S3C6410_CAMIF_IP_REV)
camif_hw_set_input_path(vp);
camif_cfg_video_path(vp);
@@ -364,7 +364,7 @@ irqreturn_t s3c_camif_irq_handler(int irq, void *priv)
camif_hw_set_test_pattern(camif, camif->test_pattern);
if (camif->variant->has_img_effect)
camif_hw_set_effect(camif, camif->colorfx,
- camif->colorfx_cb, camif->colorfx_cr);
+ camif->colorfx_cr, camif->colorfx_cb);
vp->state &= ~ST_VP_CONFIG;
}
unlock:
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c
index 664937b61fa4..8e06071a7977 100644
--- a/drivers/media/platform/s5p-cec/s5p_cec.c
+++ b/drivers/media/platform/s5p-cec/s5p_cec.c
@@ -173,6 +173,7 @@ static int s5p_cec_probe(struct platform_device *pdev)
struct platform_device *hdmi_dev;
struct resource *res;
struct s5p_cec_dev *cec;
+ bool needs_hpd = of_property_read_bool(pdev->dev.of_node, "needs-hpd");
int ret;
np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0);
@@ -221,7 +222,8 @@ static int s5p_cec_probe(struct platform_device *pdev)
cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec,
CEC_NAME,
CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
- CEC_CAP_PASSTHROUGH | CEC_CAP_RC, 1);
+ CEC_CAP_PASSTHROUGH | CEC_CAP_RC |
+ (needs_hpd ? CEC_CAP_NEEDS_HPD : 0), 1);
ret = PTR_ERR_OR_ZERO(cec->adap);
if (ret)
return ret;
@@ -235,7 +237,7 @@ static int s5p_cec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cec);
pm_runtime_enable(dev);
- dev_dbg(dev, "successfuly probed\n");
+ dev_dbg(dev, "successfully probed\n");
return 0;
err_delete_adapter:
diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/platform/s5p-cec/s5p_cec.h
index 7015845c1caa..8bcd8dc1aeb9 100644
--- a/drivers/media/platform/s5p-cec/s5p_cec.h
+++ b/drivers/media/platform/s5p-cec/s5p_cec.h
@@ -22,7 +22,6 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/timer.h>
-#include <linux/version.h>
#include <linux/workqueue.h>
#include <media/cec.h>
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 52dc7941db65..d1e3ebb22577 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -1099,10 +1099,10 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
struct s5p_jpeg_ctx *ctx)
{
int c, components = 0, notfound, n_dht = 0, n_dqt = 0;
- unsigned int height, width, word, subsampling = 0, sos = 0, sof = 0,
- sof_len = 0;
- unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER],
- dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER];
+ unsigned int height = 0, width = 0, word, subsampling = 0;
+ unsigned int sos = 0, sof = 0, sof_len = 0;
+ unsigned int dht[S5P_JPEG_MAX_MARKER], dht_len[S5P_JPEG_MAX_MARKER];
+ unsigned int dqt[S5P_JPEG_MAX_MARKER], dqt_len[S5P_JPEG_MAX_MARKER];
long length;
struct s5p_jpeg_buffer jpeg_buffer;
@@ -2642,13 +2642,13 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
if (curr_ctx->mode == S5P_JPEG_ENCODE)
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size);
v4l2_m2m_buf_done(dst_buf, state);
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs);
spin_unlock(&jpeg->slock);
s5p_jpeg_clear_int(jpeg->regs);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
return IRQ_HANDLED;
}
@@ -2707,11 +2707,12 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv)
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
}
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
if (jpeg->variant->version == SJPEG_EXYNOS4)
curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs);
spin_unlock(&jpeg->slock);
+
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
return IRQ_HANDLED;
}
@@ -2770,10 +2771,15 @@ static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id)
if (curr_ctx->mode == S5P_JPEG_ENCODE)
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload_size);
v4l2_m2m_buf_done(dst_buf, state);
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
curr_ctx->subsampling =
exynos3250_jpeg_get_subsampling_mode(jpeg->regs);
+
+ spin_unlock(&jpeg->slock);
+
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
+ return IRQ_HANDLED;
+
exit_unlock:
spin_unlock(&jpeg->slock);
return IRQ_HANDLED;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index b41ee608c171..0913881219ff 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -1293,7 +1293,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
* First set the output frame buffers
*/
if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
- mfc_err("It seems that not all destionation buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n");
+ mfc_err("It seems that not all destination buffers were mmaped\nMFC requires that all destination are mmaped before starting processing\n");
return -EAGAIN;
}
if (list_empty(&ctx->src_queue)) {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 85880e9106be..88dbb9c341ec 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -1650,7 +1650,7 @@ static inline int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
* s5p_mfc_alloc_dec_buffers(ctx); */
if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
- mfc_err("It seems that not all destionation buffers were\n"
+ mfc_err("It seems that not all destination buffers were\n"
"mmaped.MFC requires that all destination are mmaped\n"
"before starting processing.\n");
return -EAGAIN;
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 992d61a8b961..871da2a2a91c 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -229,6 +229,7 @@ static void sh_vou_stream_config(struct sh_vou_device *vou_dev)
break;
case V4L2_PIX_FMT_RGB565:
dataswap ^= 1;
+ /* fall through */
case V4L2_PIX_FMT_RGB565X:
row_coeff = 2;
break;
@@ -815,6 +816,7 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
default:
pr_warn("%s(): Invalid bus-format code %d, using default 8-bit\n",
__func__, bus_fmt);
+ /* fall through */
case SH_VOU_BUS_8BIT:
return 1;
case SH_VOU_BUS_16BIT:
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 3c9421f4d8e3..45a0429d75bb 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -23,6 +23,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -36,7 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dev.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/videobuf2-v4l2.h>
/* Default to VGA resolution */
@@ -1512,8 +1513,8 @@ static int soc_of_bind(struct soc_camera_host *ici,
if (!info)
return -ENOMEM;
- info->sasd.asd.match.of.node = remote;
- info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF;
+ info->sasd.asd.match.fwnode.fwnode = of_fwnode_handle(remote);
+ info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
info->subdev = &info->sasd.asd;
/* Or shall this be managed by the soc-camera device? */
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index e3e665e1c503..57581f626f4c 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
@@ -494,6 +494,7 @@ unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
V4L2_MBUS_HSYNC_ACTIVE_LOW);
vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ /* fall through */
case V4L2_MBUS_BT656:
pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_PCLK_SAMPLE_FALLING);
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
index 7af66860d624..2cc289e4dea1 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -104,7 +104,7 @@ static void bdisp_dbg_dump_ins(struct seq_file *s, u32 val)
if (val & BLT_INS_IRQ)
seq_puts(s, "IRQ - ");
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val)
@@ -153,7 +153,7 @@ static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val)
if (val & BLT_TTY_BIG_END)
seq_puts(s, "BigEndian - ");
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_xy(struct seq_file *s, u32 val, char *name)
@@ -230,7 +230,7 @@ static void bdisp_dbg_dump_sty(struct seq_file *s,
seq_puts(s, "BigEndian - ");
done:
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val)
@@ -247,7 +247,7 @@ static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val)
else if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SAMPLE)
seq_puts(s, "Sample Chroma");
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name)
@@ -266,7 +266,7 @@ static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name)
seq_printf(s, "V: %d(6.10) / scale~%dx0.1", inc, 1024 * 10 / inc);
done:
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name)
@@ -281,7 +281,7 @@ static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name)
seq_printf(s, "V: init=%d repeat=%d", val & 0x3FF, (val >> 12) & 7);
done:
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
}
static void bdisp_dbg_dump_ivmx(struct seq_file *s,
@@ -293,7 +293,7 @@ static void bdisp_dbg_dump_ivmx(struct seq_file *s,
seq_printf(s, "IVMX3\t0x%08X\t", c3);
if (!c0 && !c1 && !c2 && !c3) {
- seq_puts(s, "\n");
+ seq_putc(s, '\n');
return;
}
diff --git a/drivers/media/platform/sti/cec/stih-cec.c b/drivers/media/platform/sti/cec/stih-cec.c
index 39ff55145a82..dccbdaebb7a8 100644
--- a/drivers/media/platform/sti/cec/stih-cec.c
+++ b/drivers/media/platform/sti/cec/stih-cec.c
@@ -1,6 +1,6 @@
/*
* STIH4xx CEC driver
- * Copyright (C) STMicroelectronic SA 2016
+ * Copyright (C) STMicroelectronics SA 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -213,7 +213,8 @@ static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
for (i = 0; i < msg->len; i++)
writeb(msg->msg[i], cec->regs + CEC_TX_DATA_BASE + i);
- /* Start transmission, configure hardware to add start and stop bits
+ /*
+ * Start transmission, configure hardware to add start and stop bits
* Signal free time is handled by the hardware
*/
writel(CEC_TX_AUTO_SOM_EN | CEC_TX_AUTO_EOM_EN | CEC_TX_START |
@@ -225,22 +226,21 @@ static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
static void stih_tx_done(struct stih_cec *cec, u32 status)
{
if (status & CEC_TX_ERROR) {
- cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR, 0, 0, 0, 1);
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ERROR);
return;
}
if (status & CEC_TX_ARB_ERROR) {
- cec_transmit_done(cec->adap,
- CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ARB_LOST);
return;
}
if (!(status & CEC_TX_ACK_GET_STS)) {
- cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK, 0, 1, 0, 0);
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_NACK);
return;
}
- cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_OK);
}
static void stih_rx_done(struct stih_cec *cec, u32 status)
@@ -353,7 +353,7 @@ static int stih_cec_probe(struct platform_device *pdev)
cec->adap = cec_allocate_adapter(&sti_cec_adap_ops, cec,
CEC_NAME,
CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH |
- CEC_CAP_TRANSMIT, 1);
+ CEC_CAP_TRANSMIT, CEC_MAX_LOG_ADDRS);
ret = PTR_ERR_OR_ZERO(cec->adap);
if (ret)
return ret;
diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
new file mode 100644
index 000000000000..07355091376b
--- /dev/null
+++ b/drivers/media/platform/stm32/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
+obj-$(CONFIG_VIDEO_STM32_HDMI_CEC) += stm32-cec.o
diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c
new file mode 100644
index 000000000000..9ab896b01ee8
--- /dev/null
+++ b/drivers/media/platform/stm32/stm32-cec.c
@@ -0,0 +1,362 @@
+/*
+ * STM32 CEC driver
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <media/cec.h>
+
+#define CEC_NAME "stm32-cec"
+
+/* CEC registers */
+#define CEC_CR 0x0000 /* Control Register */
+#define CEC_CFGR 0x0004 /* ConFiGuration Register */
+#define CEC_TXDR 0x0008 /* Rx data Register */
+#define CEC_RXDR 0x000C /* Rx data Register */
+#define CEC_ISR 0x0010 /* Interrupt and status Register */
+#define CEC_IER 0x0014 /* Interrupt enable Register */
+
+#define TXEOM BIT(2)
+#define TXSOM BIT(1)
+#define CECEN BIT(0)
+
+#define LSTN BIT(31)
+#define OAR GENMASK(30, 16)
+#define SFTOP BIT(8)
+#define BRDNOGEN BIT(7)
+#define LBPEGEN BIT(6)
+#define BREGEN BIT(5)
+#define BRESTP BIT(4)
+#define RXTOL BIT(3)
+#define SFT GENMASK(2, 0)
+#define FULL_CFG (LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \
+ | RXTOL)
+
+#define TXACKE BIT(12)
+#define TXERR BIT(11)
+#define TXUDR BIT(10)
+#define TXEND BIT(9)
+#define TXBR BIT(8)
+#define ARBLST BIT(7)
+#define RXACKE BIT(6)
+#define RXOVR BIT(2)
+#define RXEND BIT(1)
+#define RXBR BIT(0)
+
+#define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST)
+#define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR)
+
+struct stm32_cec {
+ struct cec_adapter *adap;
+ struct device *dev;
+ struct clk *clk_cec;
+ struct clk *clk_hdmi_cec;
+ struct reset_control *rstc;
+ struct regmap *regmap;
+ int irq;
+ u32 irq_status;
+ struct cec_msg rx_msg;
+ struct cec_msg tx_msg;
+ int tx_cnt;
+};
+
+static void cec_hw_init(struct stm32_cec *cec)
+{
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0);
+
+ regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT,
+ ALL_TX_IT | ALL_RX_IT);
+
+ regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG);
+}
+
+static void stm32_tx_done(struct stm32_cec *cec, u32 status)
+{
+ if (status & (TXERR | TXUDR)) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR,
+ 0, 0, 0, 1);
+ return;
+ }
+
+ if (status & ARBLST) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST,
+ 1, 0, 0, 0);
+ return;
+ }
+
+ if (status & TXACKE) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK,
+ 0, 1, 0, 0);
+ return;
+ }
+
+ if (cec->irq_status & TXBR) {
+ /* send next byte */
+ if (cec->tx_cnt < cec->tx_msg.len)
+ regmap_write(cec->regmap, CEC_TXDR,
+ cec->tx_msg.msg[cec->tx_cnt++]);
+
+ /* TXEOM is set to command transmission of the last byte */
+ if (cec->tx_cnt == cec->tx_msg.len)
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
+ }
+
+ if (cec->irq_status & TXEND)
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+}
+
+static void stm32_rx_done(struct stm32_cec *cec, u32 status)
+{
+ if (cec->irq_status & (RXACKE | RXOVR)) {
+ cec->rx_msg.len = 0;
+ return;
+ }
+
+ if (cec->irq_status & RXBR) {
+ u32 val;
+
+ regmap_read(cec->regmap, CEC_RXDR, &val);
+ cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
+ }
+
+ if (cec->irq_status & RXEND) {
+ cec_received_msg(cec->adap, &cec->rx_msg);
+ cec->rx_msg.len = 0;
+ }
+}
+
+static irqreturn_t stm32_cec_irq_thread(int irq, void *arg)
+{
+ struct stm32_cec *cec = arg;
+
+ if (cec->irq_status & ALL_TX_IT)
+ stm32_tx_done(cec, cec->irq_status);
+
+ if (cec->irq_status & ALL_RX_IT)
+ stm32_rx_done(cec, cec->irq_status);
+
+ cec->irq_status = 0;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stm32_cec_irq_handler(int irq, void *arg)
+{
+ struct stm32_cec *cec = arg;
+
+ regmap_read(cec->regmap, CEC_ISR, &cec->irq_status);
+
+ regmap_update_bits(cec->regmap, CEC_ISR,
+ ALL_TX_IT | ALL_RX_IT,
+ ALL_TX_IT | ALL_RX_IT);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct stm32_cec *cec = adap->priv;
+ int ret = 0;
+
+ if (enable) {
+ ret = clk_enable(cec->clk_cec);
+ if (ret)
+ dev_err(cec->dev, "fail to enable cec clock\n");
+
+ clk_enable(cec->clk_hdmi_cec);
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
+ } else {
+ clk_disable(cec->clk_cec);
+ clk_disable(cec->clk_hdmi_cec);
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
+ }
+
+ return ret;
+}
+
+static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct stm32_cec *cec = adap->priv;
+ u32 oar = (1 << logical_addr) << 16;
+
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0);
+ else
+ regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar);
+
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
+
+ return 0;
+}
+
+static int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct stm32_cec *cec = adap->priv;
+
+ /* Copy message */
+ cec->tx_msg = *msg;
+ cec->tx_cnt = 0;
+
+ /*
+ * If the CEC message consists of only one byte,
+ * TXEOM must be set before of TXSOM.
+ */
+ if (cec->tx_msg.len == 1)
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
+
+ /* TXSOM is set to command transmission of the first byte */
+ regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM);
+
+ /* Write the header (first byte of message) */
+ regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]);
+ cec->tx_cnt++;
+
+ return 0;
+}
+
+static const struct cec_adap_ops stm32_cec_adap_ops = {
+ .adap_enable = stm32_cec_adap_enable,
+ .adap_log_addr = stm32_cec_adap_log_addr,
+ .adap_transmit = stm32_cec_adap_transmit,
+};
+
+static const struct regmap_config stm32_cec_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x14,
+ .fast_io = true,
+};
+
+static int stm32_cec_probe(struct platform_device *pdev)
+{
+ u32 caps = CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH |
+ CEC_CAP_TRANSMIT | CEC_CAP_RC | CEC_CAP_PHYS_ADDR |
+ CEC_MODE_MONITOR_ALL;
+ struct resource *res;
+ struct stm32_cec *cec;
+ void __iomem *mmio;
+ int ret;
+
+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio,
+ &stm32_cec_regmap_cfg);
+
+ if (IS_ERR(cec->regmap))
+ return PTR_ERR(cec->regmap);
+
+ cec->irq = platform_get_irq(pdev, 0);
+ if (cec->irq < 0)
+ return cec->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+ stm32_cec_irq_handler,
+ stm32_cec_irq_thread,
+ 0,
+ pdev->name, cec);
+ if (ret)
+ return ret;
+
+ cec->clk_cec = devm_clk_get(&pdev->dev, "cec");
+ if (IS_ERR(cec->clk_cec)) {
+ dev_err(&pdev->dev, "Cannot get cec clock\n");
+ return PTR_ERR(cec->clk_cec);
+ }
+
+ ret = clk_prepare(cec->clk_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare cec clock\n");
+ return ret;
+ }
+
+ cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
+ if (!IS_ERR(cec->clk_hdmi_cec)) {
+ ret = clk_prepare(cec->clk_hdmi_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare hdmi-cec clock\n");
+ return ret;
+ }
+ }
+
+ /*
+ * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is
+ * available for example when a drm driver can provide edid
+ */
+ cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec,
+ CEC_NAME, caps, CEC_MAX_LOG_ADDRS);
+ ret = PTR_ERR_OR_ZERO(cec->adap);
+ if (ret)
+ return ret;
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret) {
+ cec_delete_adapter(cec->adap);
+ return ret;
+ }
+
+ cec_hw_init(cec);
+
+ platform_set_drvdata(pdev, cec);
+
+ return 0;
+}
+
+static int stm32_cec_remove(struct platform_device *pdev)
+{
+ struct stm32_cec *cec = platform_get_drvdata(pdev);
+
+ clk_unprepare(cec->clk_cec);
+ clk_unprepare(cec->clk_hdmi_cec);
+
+ cec_unregister_adapter(cec->adap);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_cec_of_match[] = {
+ { .compatible = "st,stm32-cec" },
+ { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_cec_of_match);
+
+static struct platform_driver stm32_cec_driver = {
+ .probe = stm32_cec_probe,
+ .remove = stm32_cec_remove,
+ .driver = {
+ .name = CEC_NAME,
+ .of_match_table = stm32_cec_of_match,
+ },
+};
+
+module_platform_driver(stm32_cec_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
new file mode 100644
index 000000000000..83d32a5d0f40
--- /dev/null
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -0,0 +1,1404 @@
+/*
+ * Driver for STM32 Digital Camera Memory Interface
+ *
+ * Copyright (C) STMicroelectronics SA 2017
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ * Hugues Fruchet <hugues.fruchet@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * This driver is based on atmel_isi.c
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define DRV_NAME "stm32-dcmi"
+
+/* Registers offset for DCMI */
+#define DCMI_CR 0x00 /* Control Register */
+#define DCMI_SR 0x04 /* Status Register */
+#define DCMI_RIS 0x08 /* Raw Interrupt Status register */
+#define DCMI_IER 0x0C /* Interrupt Enable Register */
+#define DCMI_MIS 0x10 /* Masked Interrupt Status register */
+#define DCMI_ICR 0x14 /* Interrupt Clear Register */
+#define DCMI_ESCR 0x18 /* Embedded Synchronization Code Register */
+#define DCMI_ESUR 0x1C /* Embedded Synchronization Unmask Register */
+#define DCMI_CWSTRT 0x20 /* Crop Window STaRT */
+#define DCMI_CWSIZE 0x24 /* Crop Window SIZE */
+#define DCMI_DR 0x28 /* Data Register */
+#define DCMI_IDR 0x2C /* IDentifier Register */
+
+/* Bits definition for control register (DCMI_CR) */
+#define CR_CAPTURE BIT(0)
+#define CR_CM BIT(1)
+#define CR_CROP BIT(2)
+#define CR_JPEG BIT(3)
+#define CR_ESS BIT(4)
+#define CR_PCKPOL BIT(5)
+#define CR_HSPOL BIT(6)
+#define CR_VSPOL BIT(7)
+#define CR_FCRC_0 BIT(8)
+#define CR_FCRC_1 BIT(9)
+#define CR_EDM_0 BIT(10)
+#define CR_EDM_1 BIT(11)
+#define CR_ENABLE BIT(14)
+
+/* Bits definition for status register (DCMI_SR) */
+#define SR_HSYNC BIT(0)
+#define SR_VSYNC BIT(1)
+#define SR_FNE BIT(2)
+
+/*
+ * Bits definition for interrupt registers
+ * (DCMI_RIS, DCMI_IER, DCMI_MIS, DCMI_ICR)
+ */
+#define IT_FRAME BIT(0)
+#define IT_OVR BIT(1)
+#define IT_ERR BIT(2)
+#define IT_VSYNC BIT(3)
+#define IT_LINE BIT(4)
+
+enum state {
+ STOPPED = 0,
+ RUNNING,
+ STOPPING,
+};
+
+#define MIN_WIDTH 16U
+#define MAX_WIDTH 2048U
+#define MIN_HEIGHT 16U
+#define MAX_HEIGHT 2048U
+
+#define TIMEOUT_MS 1000
+
+struct dcmi_graph_entity {
+ struct device_node *node;
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *subdev;
+};
+
+struct dcmi_format {
+ u32 fourcc;
+ u32 mbus_code;
+ u8 bpp;
+};
+
+struct dcmi_buf {
+ struct vb2_v4l2_buffer vb;
+ bool prepared;
+ dma_addr_t paddr;
+ size_t size;
+ struct list_head list;
+};
+
+struct stm32_dcmi {
+ /* Protects the access of variables shared within the interrupt */
+ spinlock_t irqlock;
+ struct device *dev;
+ void __iomem *regs;
+ struct resource *res;
+ struct reset_control *rstc;
+ int sequence;
+ struct list_head buffers;
+ struct dcmi_buf *active;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct v4l2_async_notifier notifier;
+ struct dcmi_graph_entity entity;
+ struct v4l2_format fmt;
+
+ const struct dcmi_format **user_formats;
+ unsigned int num_user_formats;
+ const struct dcmi_format *current_fmt;
+
+ /* Protect this data structure */
+ struct mutex lock;
+ struct vb2_queue queue;
+
+ struct v4l2_fwnode_bus_parallel bus;
+ struct completion complete;
+ struct clk *mclk;
+ enum state state;
+ struct dma_chan *dma_chan;
+ dma_cookie_t dma_cookie;
+ u32 misr;
+ int errors_count;
+ int buffers_count;
+};
+
+static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct stm32_dcmi, notifier);
+}
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static int dcmi_start_capture(struct stm32_dcmi *dcmi);
+
+static void dcmi_dma_callback(void *param)
+{
+ struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
+ struct dma_chan *chan = dcmi->dma_chan;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ spin_lock(&dcmi->irqlock);
+
+ /* Check DMA status */
+ status = dmaengine_tx_status(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__);
+ break;
+ case DMA_COMPLETE:
+ dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
+
+ if (dcmi->active) {
+ struct dcmi_buf *buf = dcmi->active;
+ struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb;
+
+ vbuf->sequence = dcmi->sequence++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size);
+ vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
+ dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n",
+ vbuf->vb2_buf.index, vbuf->sequence);
+
+ dcmi->buffers_count++;
+ dcmi->active = NULL;
+ }
+
+ /* Restart a new DMA transfer with next buffer */
+ if (dcmi->state == RUNNING) {
+ if (list_empty(&dcmi->buffers)) {
+ dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer",
+ __func__);
+ dcmi->errors_count++;
+ dcmi->active = NULL;
+
+ spin_unlock(&dcmi->irqlock);
+ return;
+ }
+
+ dcmi->active = list_entry(dcmi->buffers.next,
+ struct dcmi_buf, list);
+
+ list_del_init(&dcmi->active->list);
+
+ if (dcmi_start_capture(dcmi)) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete",
+ __func__);
+
+ spin_unlock(&dcmi->irqlock);
+ return;
+ }
+
+ /* Enable capture */
+ reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE);
+ }
+
+ break;
+ default:
+ dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
+ break;
+ }
+
+ spin_unlock(&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;
+ }
+
+ /* Prepare a DMA transaction */
+ desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
+ buf->size,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n",
+ __func__, buf->size);
+ return -EINVAL;
+ }
+
+ /* 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);
+
+ dma_async_issue_pending(dcmi->dma_chan);
+
+ return 0;
+}
+
+static int dcmi_start_capture(struct stm32_dcmi *dcmi)
+{
+ int ret;
+ struct dcmi_buf *buf = dcmi->active;
+
+ if (!buf)
+ return -EINVAL;
+
+ ret = dcmi_start_dma(dcmi, buf);
+ if (ret) {
+ dcmi->errors_count++;
+ return ret;
+ }
+
+ /* Enable capture */
+ reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE);
+
+ return 0;
+}
+
+static irqreturn_t dcmi_irq_thread(int irq, void *arg)
+{
+ struct stm32_dcmi *dcmi = arg;
+
+ spin_lock(&dcmi->irqlock);
+
+ /* Stop capture is required */
+ if (dcmi->state == STOPPING) {
+ reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ dcmi->state = STOPPED;
+
+ complete(&dcmi->complete);
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+ }
+
+ if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
+ /*
+ * An overflow or an error has been detected,
+ * stop current DMA transfert & restart it
+ */
+ dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
+ __func__);
+
+ dcmi->errors_count++;
+ dmaengine_terminate_all(dcmi->dma_chan);
+
+ reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
+
+ dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
+
+ if (dcmi_start_capture(dcmi)) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
+ __func__);
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+ }
+ }
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dcmi_irq_callback(int irq, void *arg)
+{
+ struct stm32_dcmi *dcmi = arg;
+
+ spin_lock(&dcmi->irqlock);
+
+ dcmi->misr = reg_read(dcmi->regs, DCMI_MIS);
+
+ /* Clear interrupt */
+ reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
+
+ spin_unlock(&dcmi->irqlock);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int dcmi_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ unsigned int size;
+
+ size = dcmi->fmt.fmt.pix.sizeimage;
+
+ /* Make sure the image size is large enough */
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ dcmi->active = NULL;
+
+ dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n",
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int dcmi_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int dcmi_buf_prepare(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);
+ unsigned long size;
+
+ size = dcmi->fmt.fmt.pix.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(dcmi->dev, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ if (!buf->prepared) {
+ /* Get memory addresses */
+ buf->paddr =
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+ buf->prepared = true;
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
+
+ dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n",
+ vb->index, &buf->paddr, buf->size);
+ }
+
+ return 0;
+}
+
+static void dcmi_buf_queue(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);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dcmi->irqlock, flags);
+
+ if ((dcmi->state == RUNNING) && (!dcmi->active)) {
+ dcmi->active = buf;
+
+ dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
+ buf->vb.vb2_buf.index);
+
+ if (dcmi_start_capture(dcmi)) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
+ __func__);
+
+ spin_unlock_irqrestore(&dcmi->irqlock, flags);
+ return;
+ }
+ } else {
+ /* Enqueue to video buffers list */
+ list_add_tail(&buf->list, &dcmi->buffers);
+ }
+
+ spin_unlock_irqrestore(&dcmi->irqlock, flags);
+}
+
+static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ struct dcmi_buf *buf, *node;
+ u32 val;
+ int ret;
+
+ ret = clk_enable(dcmi->mclk);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock",
+ __func__);
+ goto err_release_buffers;
+ }
+
+ /* Enable stream on the sub device */
+ ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error",
+ __func__);
+ goto err_disable_clock;
+ }
+
+ spin_lock_irq(&dcmi->irqlock);
+
+ val = reg_read(dcmi->regs, DCMI_CR);
+
+ val &= ~(CR_PCKPOL | CR_HSPOL | CR_VSPOL |
+ CR_EDM_0 | CR_EDM_1 | CR_FCRC_0 |
+ CR_FCRC_1 | CR_JPEG | CR_ESS);
+
+ /* Set bus width */
+ switch (dcmi->bus.bus_width) {
+ case 14:
+ val &= CR_EDM_0 + CR_EDM_1;
+ break;
+ case 12:
+ val &= CR_EDM_1;
+ break;
+ case 10:
+ val &= CR_EDM_0;
+ break;
+ default:
+ /* Set bus width to 8 bits by default */
+ break;
+ }
+
+ /* Set vertical synchronization polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ val |= CR_VSPOL;
+
+ /* Set horizontal synchronization polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ val |= CR_HSPOL;
+
+ /* Set pixel clock polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ val |= CR_PCKPOL;
+
+ reg_write(dcmi->regs, DCMI_CR, val);
+
+ /* Enable dcmi */
+ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
+
+ dcmi->state = RUNNING;
+
+ dcmi->sequence = 0;
+ dcmi->errors_count = 0;
+ dcmi->buffers_count = 0;
+ dcmi->active = NULL;
+
+ /*
+ * Start transfer if at least one buffer has been queued,
+ * otherwise transfer is deferred at buffer queueing
+ */
+ if (list_empty(&dcmi->buffers)) {
+ dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer queueing\n");
+ spin_unlock_irq(&dcmi->irqlock);
+ return 0;
+ }
+
+ dcmi->active = list_entry(dcmi->buffers.next, struct dcmi_buf, list);
+ list_del_init(&dcmi->active->list);
+
+ dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
+
+ ret = dcmi_start_capture(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture",
+ __func__);
+
+ spin_unlock_irq(&dcmi->irqlock);
+ goto err_subdev_streamoff;
+ }
+
+ /* Enable interruptions */
+ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ spin_unlock_irq(&dcmi->irqlock);
+
+ return 0;
+
+err_subdev_streamoff:
+ v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
+
+err_disable_clock:
+ clk_disable(dcmi->mclk);
+
+err_release_buffers:
+ spin_lock_irq(&dcmi->irqlock);
+ /*
+ * Return all buffers to vb2 in QUEUED state.
+ * This will give ownership back to userspace
+ */
+ if (dcmi->active) {
+ buf = dcmi->active;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ dcmi->active = NULL;
+ }
+ list_for_each_entry_safe(buf, node, &dcmi->buffers, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irq(&dcmi->irqlock);
+
+ return ret;
+}
+
+static void dcmi_stop_streaming(struct vb2_queue *vq)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ struct dcmi_buf *buf, *node;
+ unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS);
+ long timeout;
+ int ret;
+
+ /* Disable stream on the sub device */
+ ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
+ if (ret && ret != -ENOIOCTLCMD)
+ dev_err(dcmi->dev, "stream off failed in subdev\n");
+
+ dcmi->state = STOPPING;
+
+ timeout = wait_for_completion_interruptible_timeout(&dcmi->complete,
+ time_ms);
+
+ spin_lock_irq(&dcmi->irqlock);
+
+ /* Disable interruptions */
+ reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ /* Disable DCMI */
+ reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE);
+
+ if (!timeout) {
+ dev_err(dcmi->dev, "Timeout during stop streaming\n");
+ dcmi->state = STOPPED;
+ }
+
+ /* Return all queued buffers to vb2 in ERROR state */
+ if (dcmi->active) {
+ buf = dcmi->active;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ dcmi->active = NULL;
+ }
+ list_for_each_entry_safe(buf, node, &dcmi->buffers, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irq(&dcmi->irqlock);
+
+ /* Stop all pending DMA operations */
+ dmaengine_terminate_all(dcmi->dma_chan);
+
+ clk_disable(dcmi->mclk);
+
+ dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
+ dcmi->errors_count, dcmi->buffers_count);
+}
+
+static struct vb2_ops dcmi_video_qops = {
+ .queue_setup = dcmi_queue_setup,
+ .buf_init = dcmi_buf_init,
+ .buf_prepare = dcmi_buf_prepare,
+ .buf_queue = dcmi_buf_queue,
+ .start_streaming = dcmi_start_streaming,
+ .stop_streaming = dcmi_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int dcmi_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ *fmt = dcmi->fmt;
+
+ return 0;
+}
+
+static const struct dcmi_format *find_format_by_fourcc(struct stm32_dcmi *dcmi,
+ unsigned int fourcc)
+{
+ unsigned int num_formats = dcmi->num_user_formats;
+ const struct dcmi_format *fmt;
+ unsigned int i;
+
+ for (i = 0; i < num_formats; i++) {
+ fmt = dcmi->user_formats[i];
+ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
+ const struct dcmi_format **current_fmt)
+{
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ int ret;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, pixfmt->pixelformat);
+ if (!dcmi_fmt) {
+ dcmi_fmt = dcmi->user_formats[dcmi->num_user_formats - 1];
+ pixfmt->pixelformat = dcmi_fmt->fourcc;
+ }
+
+ /* Limit to hardware capabilities */
+ pixfmt->width = clamp(pixfmt->width, MIN_WIDTH, MAX_WIDTH);
+ pixfmt->height = clamp(pixfmt->height, MIN_HEIGHT, MAX_HEIGHT);
+
+ v4l2_fill_mbus_format(&format.format, pixfmt, dcmi_fmt->mbus_code);
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
+ &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ v4l2_fill_pix_format(pixfmt, &format.format);
+
+ pixfmt->field = V4L2_FIELD_NONE;
+ pixfmt->bytesperline = pixfmt->width * dcmi_fmt->bpp;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ if (current_fmt)
+ *current_fmt = dcmi_fmt;
+
+ return 0;
+}
+
+static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
+{
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ const struct dcmi_format *current_fmt;
+ int ret;
+
+ ret = dcmi_try_fmt(dcmi, f, &current_fmt);
+ if (ret)
+ return ret;
+
+ v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
+ current_fmt->mbus_code);
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
+ set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ dcmi->fmt = *f;
+ dcmi->current_fmt = current_fmt;
+
+ return 0;
+}
+
+static int dcmi_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ if (vb2_is_streaming(&dcmi->queue))
+ return -EBUSY;
+
+ return dcmi_set_fmt(dcmi, f);
+}
+
+static int dcmi_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ return dcmi_try_fmt(dcmi, f, NULL);
+}
+
+static int dcmi_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ if (f->index >= dcmi->num_user_formats)
+ return -EINVAL;
+
+ f->pixelformat = dcmi->user_formats[f->index]->fourcc;
+ return 0;
+}
+
+static int dcmi_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, "STM32 Camera Memory Interface",
+ sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info));
+ return 0;
+}
+
+static int dcmi_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(i->name, "Camera", sizeof(i->name));
+ return 0;
+}
+
+static int dcmi_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int dcmi_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int dcmi_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .index = fsize->index,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, fsize->pixel_format);
+ if (!dcmi_fmt)
+ return -EINVAL;
+
+ fse.code = dcmi_fmt->mbus_code;
+
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int dcmi_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, fival->pixel_format);
+ if (!dcmi_fmt)
+ return -EINVAL;
+
+ fie.code = dcmi_fmt->mbus_code;
+
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
+ enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static const struct of_device_id stm32_dcmi_of_match[] = {
+ { .compatible = "st,stm32-dcmi"},
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match);
+
+static int dcmi_open(struct file *file)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ struct v4l2_subdev *sd = dcmi->entity.subdev;
+ int ret;
+
+ if (mutex_lock_interruptible(&dcmi->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ if (!v4l2_fh_is_singular_file(file))
+ goto fh_rel;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto fh_rel;
+
+ ret = dcmi_set_fmt(dcmi, &dcmi->fmt);
+ if (ret)
+ v4l2_subdev_call(sd, core, s_power, 0);
+fh_rel:
+ if (ret)
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&dcmi->lock);
+ return ret;
+}
+
+static int dcmi_release(struct file *file)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ struct v4l2_subdev *sd = dcmi->entity.subdev;
+ bool fh_singular;
+ int ret;
+
+ mutex_lock(&dcmi->lock);
+
+ fh_singular = v4l2_fh_is_singular_file(file);
+
+ ret = _vb2_fop_release(file, NULL);
+
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+ mutex_unlock(&dcmi->lock);
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops dcmi_ioctl_ops = {
+ .vidioc_querycap = dcmi_querycap,
+
+ .vidioc_try_fmt_vid_cap = dcmi_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = dcmi_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dcmi_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = dcmi_enum_fmt_vid_cap,
+
+ .vidioc_enum_input = dcmi_enum_input,
+ .vidioc_g_input = dcmi_g_input,
+ .vidioc_s_input = dcmi_s_input,
+
+ .vidioc_enum_framesizes = dcmi_enum_framesizes,
+ .vidioc_enum_frameintervals = dcmi_enum_frameintervals,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations dcmi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = dcmi_open,
+ .release = dcmi_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = vb2_fop_get_unmapped_area,
+#endif
+ .read = vb2_fop_read,
+};
+
+static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi)
+{
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = CIF_WIDTH,
+ .height = CIF_HEIGHT,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = dcmi->user_formats[0]->fourcc,
+ },
+ };
+ int ret;
+
+ ret = dcmi_try_fmt(dcmi, &f, NULL);
+ if (ret)
+ return ret;
+ dcmi->current_fmt = dcmi->user_formats[0];
+ dcmi->fmt = f;
+ return 0;
+}
+
+static const struct dcmi_format dcmi_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .bpp = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .bpp = 2,
+ },
+};
+
+static int dcmi_formats_init(struct stm32_dcmi *dcmi)
+{
+ const struct dcmi_format *dcmi_fmts[ARRAY_SIZE(dcmi_formats)];
+ unsigned int num_fmts = 0, i, j;
+ struct v4l2_subdev *subdev = dcmi->entity.subdev;
+ struct v4l2_subdev_mbus_code_enum mbus_code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ while (!v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code)) {
+ for (i = 0; i < ARRAY_SIZE(dcmi_formats); i++) {
+ if (dcmi_formats[i].mbus_code != mbus_code.code)
+ continue;
+
+ /* Code supported, have we got this fourcc yet? */
+ for (j = 0; j < num_fmts; j++)
+ if (dcmi_fmts[j]->fourcc ==
+ dcmi_formats[i].fourcc)
+ /* Already available */
+ break;
+ if (j == num_fmts)
+ /* New */
+ dcmi_fmts[num_fmts++] = dcmi_formats + i;
+ }
+ mbus_code.index++;
+ }
+
+ if (!num_fmts)
+ return -ENXIO;
+
+ dcmi->num_user_formats = num_fmts;
+ dcmi->user_formats = devm_kcalloc(dcmi->dev,
+ num_fmts, sizeof(struct dcmi_format *),
+ GFP_KERNEL);
+ if (!dcmi->user_formats) {
+ dev_err(dcmi->dev, "could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ memcpy(dcmi->user_formats, dcmi_fmts,
+ num_fmts * sizeof(struct dcmi_format *));
+ dcmi->current_fmt = dcmi->user_formats[0];
+
+ return 0;
+}
+
+static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+ int ret;
+
+ dcmi->vdev->ctrl_handler = dcmi->entity.subdev->ctrl_handler;
+ ret = dcmi_formats_init(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "No supported mediabus format found\n");
+ return ret;
+ }
+
+ ret = dcmi_set_default_fmt(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "Could not set default format\n");
+ return ret;
+ }
+
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ dev_err(dcmi->dev, "Failed to register video device\n");
+ return ret;
+ }
+
+ dev_dbg(dcmi->dev, "Device registered as %s\n",
+ video_device_node_name(dcmi->vdev));
+ return 0;
+}
+
+static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+
+ dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev));
+
+ /* Checks internaly if vdev has been init or not */
+ video_unregister_device(dcmi->vdev);
+}
+
+static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+
+ dev_dbg(dcmi->dev, "Subdev %s bound\n", subdev->name);
+
+ dcmi->entity.subdev = subdev;
+
+ return 0;
+}
+
+static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node)
+{
+ struct device_node *ep = NULL;
+ struct device_node *remote;
+
+ while (1) {
+ ep = of_graph_get_next_endpoint(node, ep);
+ if (!ep)
+ return -EINVAL;
+
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote) {
+ of_node_put(ep);
+ return -EINVAL;
+ }
+
+ /* Remote node to connect */
+ dcmi->entity.node = remote;
+ dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ dcmi->entity.asd.match.fwnode.fwnode = of_fwnode_handle(remote);
+ return 0;
+ }
+}
+
+static int dcmi_graph_init(struct stm32_dcmi *dcmi)
+{
+ struct v4l2_async_subdev **subdevs = NULL;
+ int ret;
+
+ /* Parse the graph to extract a list of subdevice DT nodes. */
+ ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "Graph parsing failed\n");
+ return ret;
+ }
+
+ /* Register the subdevices notifier. */
+ subdevs = devm_kzalloc(dcmi->dev, sizeof(*subdevs), GFP_KERNEL);
+ if (!subdevs) {
+ of_node_put(dcmi->entity.node);
+ return -ENOMEM;
+ }
+
+ subdevs[0] = &dcmi->entity.asd;
+
+ dcmi->notifier.subdevs = subdevs;
+ dcmi->notifier.num_subdevs = 1;
+ dcmi->notifier.bound = dcmi_graph_notify_bound;
+ dcmi->notifier.unbind = dcmi_graph_notify_unbind;
+ dcmi->notifier.complete = dcmi_graph_notify_complete;
+
+ ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "Notifier registration failed\n");
+ of_node_put(dcmi->entity.node);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match = NULL;
+ struct v4l2_fwnode_endpoint ep;
+ struct stm32_dcmi *dcmi;
+ struct vb2_queue *q;
+ struct dma_chan *chan;
+ struct clk *mclk;
+ int irq;
+ int ret = 0;
+
+ match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Could not find a match in devicetree\n");
+ return -ENODEV;
+ }
+
+ dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL);
+ if (!dcmi)
+ return -ENOMEM;
+
+ dcmi->rstc = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(dcmi->rstc)) {
+ dev_err(&pdev->dev, "Could not get reset control\n");
+ return -ENODEV;
+ }
+
+ /* Get bus characteristics from devicetree */
+ np = of_graph_get_next_endpoint(np, NULL);
+ if (!np) {
+ dev_err(&pdev->dev, "Could not find the endpoint\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not parse the endpoint\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ if (ep.bus_type == V4L2_MBUS_CSI2) {
+ dev_err(&pdev->dev, "CSI bus not supported\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+ dcmi->bus.flags = ep.bus.parallel.flags;
+ dcmi->bus.bus_width = ep.bus.parallel.bus_width;
+ dcmi->bus.data_shift = ep.bus.parallel.data_shift;
+
+ of_node_put(np);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "Could not get irq\n");
+ return -ENODEV;
+ }
+
+ dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dcmi->res) {
+ dev_err(&pdev->dev, "Could not get resource\n");
+ return -ENODEV;
+ }
+
+ dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res);
+ if (IS_ERR(dcmi->regs)) {
+ dev_err(&pdev->dev, "Could not map registers\n");
+ return PTR_ERR(dcmi->regs);
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, dcmi_irq_callback,
+ dcmi_irq_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), dcmi);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ return -ENODEV;
+ }
+
+ mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(mclk)) {
+ dev_err(&pdev->dev, "Unable to get mclk\n");
+ return PTR_ERR(mclk);
+ }
+
+ chan = dma_request_slave_channel(&pdev->dev, "tx");
+ if (!chan) {
+ dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = clk_prepare(mclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk);
+ goto err_dma_release;
+ }
+
+ spin_lock_init(&dcmi->irqlock);
+ mutex_init(&dcmi->lock);
+ init_completion(&dcmi->complete);
+ INIT_LIST_HEAD(&dcmi->buffers);
+
+ dcmi->dev = &pdev->dev;
+ dcmi->mclk = mclk;
+ dcmi->state = STOPPED;
+ dcmi->dma_chan = chan;
+
+ q = &dcmi->queue;
+
+ /* Initialize the top-level structure */
+ ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev);
+ if (ret)
+ goto err_clk_unprepare;
+
+ dcmi->vdev = video_device_alloc();
+ if (!dcmi->vdev) {
+ ret = -ENOMEM;
+ goto err_device_unregister;
+ }
+
+ /* Video node */
+ dcmi->vdev->fops = &dcmi_fops;
+ dcmi->vdev->v4l2_dev = &dcmi->v4l2_dev;
+ dcmi->vdev->queue = &dcmi->queue;
+ strlcpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name));
+ dcmi->vdev->release = video_device_release;
+ dcmi->vdev->ioctl_ops = &dcmi_ioctl_ops;
+ dcmi->vdev->lock = &dcmi->lock;
+ dcmi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ video_set_drvdata(dcmi->vdev, dcmi);
+
+ /* Buffer queue */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
+ q->lock = &dcmi->lock;
+ q->drv_priv = dcmi;
+ q->buf_struct_size = sizeof(struct dcmi_buf);
+ q->ops = &dcmi_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+ q->dev = &pdev->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize vb2 queue\n");
+ goto err_device_release;
+ }
+
+ ret = dcmi_graph_init(dcmi);
+ if (ret < 0)
+ goto err_device_release;
+
+ /* Reset device */
+ ret = reset_control_assert(dcmi->rstc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to assert the reset line\n");
+ goto err_device_release;
+ }
+
+ usleep_range(3000, 5000);
+
+ ret = reset_control_deassert(dcmi->rstc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to deassert the reset line\n");
+ goto err_device_release;
+ }
+
+ dev_info(&pdev->dev, "Probe done\n");
+
+ platform_set_drvdata(pdev, dcmi);
+ return 0;
+
+err_device_release:
+ video_device_release(dcmi->vdev);
+err_device_unregister:
+ v4l2_device_unregister(&dcmi->v4l2_dev);
+err_clk_unprepare:
+ clk_unprepare(dcmi->mclk);
+err_dma_release:
+ dma_release_channel(dcmi->dma_chan);
+
+ return ret;
+}
+
+static int dcmi_remove(struct platform_device *pdev)
+{
+ struct stm32_dcmi *dcmi = platform_get_drvdata(pdev);
+
+ v4l2_async_notifier_unregister(&dcmi->notifier);
+ v4l2_device_unregister(&dcmi->v4l2_dev);
+ clk_unprepare(dcmi->mclk);
+ dma_release_channel(dcmi->dma_chan);
+
+ return 0;
+}
+
+static struct platform_driver stm32_dcmi_driver = {
+ .probe = dcmi_probe,
+ .remove = dcmi_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(stm32_dcmi_of_match),
+ },
+};
+
+module_platform_driver(stm32_dcmi_driver);
+
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Hugues Fruchet <hugues.fruchet@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
index 7a058b6e03d0..177faa36bc16 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -21,7 +21,7 @@
#include <linux/of_device.h>
#include <linux/of_graph.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
@@ -270,7 +270,7 @@ struct cal_ctx {
struct video_device vdev;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *sensor;
- struct v4l2_of_endpoint endpoint;
+ struct v4l2_fwnode_endpoint endpoint;
struct v4l2_async_subdev asd;
struct v4l2_async_subdev *asd_list[1];
@@ -608,7 +608,8 @@ static void csi2_lane_config(struct cal_ctx *ctx)
u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
- struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2;
+ struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
+ &ctx->endpoint.bus.mipi_csi2;
int lane;
set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
@@ -1643,7 +1644,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
struct platform_device *pdev = ctx->dev->pdev;
struct device_node *ep_node, *port, *remote_ep,
*sensor_node, *parent;
- struct v4l2_of_endpoint *endpoint;
+ struct v4l2_fwnode_endpoint *endpoint;
struct v4l2_async_subdev *asd;
u32 regval = 0;
int ret, index, found_port = 0, lane;
@@ -1698,15 +1699,15 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
ctx_dbg(3, ctx, "can't get remote parent\n");
goto cleanup_exit;
}
- asd->match_type = V4L2_ASYNC_MATCH_OF;
- asd->match.of.node = sensor_node;
+ asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+ asd->match.fwnode.fwnode = of_fwnode_handle(sensor_node);
remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
if (!remote_ep) {
ctx_dbg(3, ctx, "can't get remote-endpoint\n");
goto cleanup_exit;
}
- v4l2_of_parse_endpoint(remote_ep, endpoint);
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), endpoint);
if (endpoint->bus_type != V4L2_MBUS_CSI2) {
ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n",
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
new file mode 100644
index 000000000000..665744716f73
--- /dev/null
+++ b/drivers/media/platform/video-mux.c
@@ -0,0 +1,334 @@
+/*
+ * video stream multiplexer controlled via mux control
+ *
+ * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
+ * Copyright (C) 2016-2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+struct video_mux {
+ struct v4l2_subdev subdev;
+ struct media_pad *pads;
+ struct v4l2_mbus_framefmt *format_mbus;
+ struct regmap_field *field;
+ struct mutex lock;
+ int active;
+};
+
+static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct video_mux, subdev);
+}
+
+static int video_mux_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ int ret = 0;
+
+ /*
+ * The mux state is determined by the enabled sink pad link.
+ * Enabling or disabling the source pad link has no effect.
+ */
+ if (local->flags & MEDIA_PAD_FL_SOURCE)
+ return 0;
+
+ dev_dbg(sd->dev, "link setup '%s':%d->'%s':%d[%d]",
+ remote->entity->name, remote->index, local->entity->name,
+ local->index, flags & MEDIA_LNK_FL_ENABLED);
+
+ mutex_lock(&vmux->lock);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (vmux->active == local->index)
+ goto out;
+
+ if (vmux->active >= 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ dev_dbg(sd->dev, "setting %d active\n", local->index);
+ ret = regmap_field_write(vmux->field, local->index);
+ if (ret < 0)
+ goto out;
+ vmux->active = local->index;
+ } else {
+ if (vmux->active != local->index)
+ goto out;
+
+ dev_dbg(sd->dev, "going inactive\n");
+ vmux->active = -1;
+ }
+
+out:
+ mutex_unlock(&vmux->lock);
+ return ret;
+}
+
+static const struct media_entity_operations video_mux_ops = {
+ .link_setup = video_mux_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int video_mux_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ struct v4l2_subdev *upstream_sd;
+ struct media_pad *pad;
+
+ if (vmux->active == -1) {
+ dev_err(sd->dev, "Can not start streaming on inactive mux\n");
+ return -EINVAL;
+ }
+
+ pad = media_entity_remote_pad(&sd->entity.pads[vmux->active]);
+ if (!pad) {
+ dev_err(sd->dev, "Failed to find remote source pad\n");
+ return -ENOLINK;
+ }
+
+ if (!is_media_entity_v4l2_subdev(pad->entity)) {
+ dev_err(sd->dev, "Upstream entity is not a v4l2 subdev\n");
+ return -ENODEV;
+ }
+
+ upstream_sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ return v4l2_subdev_call(upstream_sd, video, s_stream, enable);
+}
+
+static const struct v4l2_subdev_video_ops video_mux_subdev_video_ops = {
+ .s_stream = video_mux_s_stream,
+};
+
+static struct v4l2_mbus_framefmt *
+__video_mux_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &vmux->format_mbus[pad];
+ default:
+ return NULL;
+ }
+}
+
+static int video_mux_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+
+ mutex_lock(&vmux->lock);
+
+ sdformat->format = *__video_mux_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+
+ mutex_unlock(&vmux->lock);
+
+ return 0;
+}
+
+static int video_mux_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ struct v4l2_mbus_framefmt *mbusformat;
+ struct media_pad *pad = &vmux->pads[sdformat->pad];
+
+ mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+ if (!mbusformat)
+ return -EINVAL;
+
+ mutex_lock(&vmux->lock);
+
+ /* Source pad mirrors active sink pad, no limitations on sink pads */
+ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0)
+ sdformat->format = vmux->format_mbus[vmux->active];
+
+ *mbusformat = sdformat->format;
+
+ mutex_unlock(&vmux->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops video_mux_pad_ops = {
+ .get_fmt = video_mux_get_format,
+ .set_fmt = video_mux_set_format,
+};
+
+static const struct v4l2_subdev_ops video_mux_subdev_ops = {
+ .pad = &video_mux_pad_ops,
+ .video = &video_mux_subdev_video_ops,
+};
+
+static int video_mux_probe_mmio_mux(struct video_mux *vmux)
+{
+ struct device *dev = vmux->subdev.dev;
+ struct of_phandle_args args;
+ struct reg_field field;
+ struct regmap *regmap;
+ u32 reg, mask;
+ int ret;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "mux-controls",
+ "#mux-control-cells", 0, &args);
+ if (ret)
+ return ret;
+
+ if (!of_device_is_compatible(args.np, "mmio-mux"))
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(args.np->parent);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = of_property_read_u32_index(args.np, "mux-reg-masks",
+ 2 * args.args[0], &reg);
+ if (!ret)
+ ret = of_property_read_u32_index(args.np, "mux-reg-masks",
+ 2 * args.args[0] + 1, &mask);
+ if (ret < 0)
+ return ret;
+
+ field.reg = reg;
+ field.msb = fls(mask) - 1;
+ field.lsb = ffs(mask) - 1;
+
+ vmux->field = devm_regmap_field_alloc(dev, regmap, field);
+ if (IS_ERR(vmux->field))
+ return PTR_ERR(vmux->field);
+
+ return 0;
+}
+
+static int video_mux_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct device_node *ep;
+ struct video_mux *vmux;
+ unsigned int num_pads = 0;
+ int ret;
+ int i;
+
+ vmux = devm_kzalloc(dev, sizeof(*vmux), GFP_KERNEL);
+ if (!vmux)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vmux);
+
+ v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops);
+ snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%s", np->name);
+ vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vmux->subdev.dev = dev;
+
+ /*
+ * The largest numbered port is the output port. It determines
+ * total number of pads.
+ */
+ for_each_endpoint_of_node(np, ep) {
+ struct of_endpoint endpoint;
+
+ of_graph_parse_endpoint(ep, &endpoint);
+ num_pads = max(num_pads, endpoint.port + 1);
+ }
+
+ if (num_pads < 2) {
+ dev_err(dev, "Not enough ports %d\n", num_pads);
+ return -EINVAL;
+ }
+
+ ret = video_mux_probe_mmio_mux(vmux);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get mux: %d\n", ret);
+ return ret;
+ }
+
+ mutex_init(&vmux->lock);
+ vmux->active = -1;
+ vmux->pads = devm_kcalloc(dev, num_pads, sizeof(*vmux->pads),
+ GFP_KERNEL);
+ vmux->format_mbus = devm_kcalloc(dev, num_pads,
+ sizeof(*vmux->format_mbus),
+ GFP_KERNEL);
+
+ for (i = 0; i < num_pads - 1; i++)
+ vmux->pads[i].flags = MEDIA_PAD_FL_SINK;
+ vmux->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
+
+ vmux->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
+ ret = media_entity_pads_init(&vmux->subdev.entity, num_pads,
+ vmux->pads);
+ if (ret < 0)
+ return ret;
+
+ vmux->subdev.entity.ops = &video_mux_ops;
+
+ return v4l2_async_register_subdev(&vmux->subdev);
+}
+
+static int video_mux_remove(struct platform_device *pdev)
+{
+ struct video_mux *vmux = platform_get_drvdata(pdev);
+ struct v4l2_subdev *sd = &vmux->subdev;
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct of_device_id video_mux_dt_ids[] = {
+ { .compatible = "video-mux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, video_mux_dt_ids);
+
+static struct platform_driver video_mux_driver = {
+ .probe = video_mux_probe,
+ .remove = video_mux_remove,
+ .driver = {
+ .of_match_table = video_mux_dt_ids,
+ .name = "video-mux",
+ },
+};
+
+module_platform_driver(video_mux_driver);
+
+MODULE_DESCRIPTION("video stream multiplexer");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_AUTHOR("Philipp Zabel, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
index a18f6352c422..71c9fe7d3370 100644
--- a/drivers/media/platform/vimc/Kconfig
+++ b/drivers/media/platform/vimc/Kconfig
@@ -2,6 +2,7 @@ config VIDEO_VIMC
tristate "Virtual Media Controller Driver (VIMC)"
depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_VMALLOC
+ select VIDEO_V4L2_TPG
default n
---help---
Skeleton driver for Virtual Media Controller
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
index c45195e5e05c..68c5d9804c11 100644
--- a/drivers/media/platform/vimc/Makefile
+++ b/drivers/media/platform/vimc/Makefile
@@ -1,3 +1,9 @@
-vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.o
+vimc-objs := vimc-core.o
+vimc_capture-objs := vimc-capture.o
+vimc_common-objs := vimc-common.o
+vimc_debayer-objs := vimc-debayer.o
+vimc_scaler-objs := vimc-scaler.o
+vimc_sensor-objs := vimc-sensor.o
-obj-$(CONFIG_VIDEO_VIMC) += vimc.o
+obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \
+ vimc_scaler.o vimc_sensor.o
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 9adb06d7e13d..88a1e5670c72 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -15,15 +15,21 @@
*
*/
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h>
-#include "vimc-capture.h"
+#include "vimc-common.h"
+
+#define VIMC_CAP_DRV_NAME "vimc-capture"
struct vimc_cap_device {
struct vimc_ent_device ved;
struct video_device vdev;
+ struct device *dev;
struct v4l2_pix_format format;
struct vb2_queue queue;
struct list_head buf_list;
@@ -40,6 +46,14 @@ struct vimc_cap_device {
struct media_pipeline pipe;
};
+static const struct v4l2_pix_format fmt_default = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+};
+
struct vimc_cap_buffer {
/*
* struct vb2_v4l2_buffer must be the first element
@@ -64,7 +78,16 @@ static int vimc_cap_querycap(struct file *file, void *priv,
return 0;
}
-static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+static void vimc_cap_get_format(struct vimc_ent_device *ved,
+ struct v4l2_pix_format *fmt)
+{
+ struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+ ved);
+
+ *fmt = vcap->format;
+}
+
+static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vimc_cap_device *vcap = video_drvdata(file);
@@ -74,16 +97,98 @@ static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *format = &f->fmt.pix;
+ const struct vimc_pix_map *vpix;
+
+ format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH,
+ VIMC_FRAME_MAX_WIDTH) & ~1;
+ format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT,
+ VIMC_FRAME_MAX_HEIGHT) & ~1;
+
+ /* Don't accept a pixelformat that is not on the table */
+ vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
+ if (!vpix) {
+ format->pixelformat = fmt_default.pixelformat;
+ vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
+ }
+ /* TODO: Add support for custom bytesperline values */
+ format->bytesperline = format->width * vpix->bpp;
+ format->sizeimage = format->bytesperline * format->height;
+
+ if (format->field == V4L2_FIELD_ANY)
+ format->field = fmt_default.field;
+
+ vimc_colorimetry_clamp(format);
+
+ return 0;
+}
+
+static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vimc_cap_device *vcap = video_drvdata(file);
+
+ /* Do not change the format while stream is on */
+ if (vb2_is_busy(&vcap->queue))
+ return -EBUSY;
+
+ vimc_cap_try_fmt_vid_cap(file, priv, f);
+
+ dev_dbg(vcap->dev, "%s: format update: "
+ "old:%dx%d (0x%x, %d, %d, %d, %d) "
+ "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name,
+ /* old */
+ vcap->format.width, vcap->format.height,
+ vcap->format.pixelformat, vcap->format.colorspace,
+ vcap->format.quantization, vcap->format.xfer_func,
+ vcap->format.ycbcr_enc,
+ /* new */
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.pixelformat, f->fmt.pix.colorspace,
+ f->fmt.pix.quantization, f->fmt.pix.xfer_func,
+ f->fmt.pix.ycbcr_enc);
+
+ vcap->format = f->fmt.pix;
+
+ return 0;
+}
+
static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct vimc_cap_device *vcap = video_drvdata(file);
+ const struct vimc_pix_map *vpix = vimc_pix_map_by_index(f->index);
+
+ if (!vpix)
+ return -EINVAL;
+
+ f->pixelformat = vpix->pixelformat;
+
+ return 0;
+}
+
+static int vimc_cap_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct vimc_pix_map *vpix;
+
+ if (fsize->index)
+ return -EINVAL;
- if (f->index > 0)
+ /* Only accept code in the pix map table */
+ vpix = vimc_pix_map_by_code(fsize->pixel_format);
+ if (!vpix)
return -EINVAL;
- /* We only support one format for now */
- f->pixelformat = vcap->format.pixelformat;
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH;
+ fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH;
+ fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT;
+ fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 2;
return 0;
}
@@ -101,10 +206,11 @@ static const struct v4l2_file_operations vimc_cap_fops = {
static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
.vidioc_querycap = vimc_cap_querycap,
- .vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = vimc_cap_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
@@ -132,31 +238,6 @@ static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
spin_unlock(&vcap->qlock);
}
-static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
-{
- struct v4l2_subdev *sd;
- struct media_pad *pad;
- int ret;
-
- /* Start the stream in the subdevice direct connected */
- pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
-
- /*
- * if it is a raw node from vimc-core, there is nothing to activate
- * TODO: remove this when there are no more raw nodes in the
- * core and return error instead
- */
- if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
- return 0;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
- ret = v4l2_subdev_call(sd, video, s_stream, enable);
- if (ret && ret != -ENOIOCTLCMD)
- return ret;
-
- return 0;
-}
-
static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
@@ -173,7 +254,7 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
}
/* Enable streaming from the pipe */
- ret = vimc_cap_pipeline_s_stream(vcap, 1);
+ ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1);
if (ret) {
media_pipeline_stop(entity);
vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
@@ -192,7 +273,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq)
struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
/* Disable streaming from the pipe */
- vimc_cap_pipeline_s_stream(vcap, 0);
+ vimc_pipeline_s_stream(&vcap->vdev.entity, 0);
/* Stop the media pipeline */
media_pipeline_stop(&vcap->vdev.entity);
@@ -234,8 +315,7 @@ static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
unsigned long size = vcap->format.sizeimage;
if (vb2_plane_size(vb, 0) < size) {
- dev_err(vcap->vdev.v4l2_dev->dev,
- "%s: buffer too small (%lu < %lu)\n",
+ dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n",
vcap->vdev.name, vb2_plane_size(vb, 0), size);
return -EINVAL;
}
@@ -256,78 +336,14 @@ static const struct vb2_ops vimc_cap_qops = {
.wait_finish = vb2_ops_wait_finish,
};
-/*
- * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
- * maybe the v4l2 function should be public
- */
-static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
-
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
-
- return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
-}
-
-static int vimc_cap_link_validate(struct media_link *link)
-{
- struct v4l2_subdev_format source_fmt;
- const struct vimc_pix_map *vpix;
- struct vimc_cap_device *vcap = container_of(link->sink->entity,
- struct vimc_cap_device,
- vdev.entity);
- struct v4l2_pix_format *sink_fmt = &vcap->format;
- int ret;
-
- /*
- * if it is a raw node from vimc-core, ignore the link for now
- * TODO: remove this when there are no more raw nodes in the
- * core and return error instead
- */
- if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
- return 0;
-
- /* Get the the format of the subdev */
- ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
- &source_fmt);
- if (ret)
- return ret;
-
- dev_dbg(vcap->vdev.v4l2_dev->dev,
- "%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
- vcap->vdev.name,
- source_fmt.format.width, source_fmt.format.height,
- source_fmt.format.code,
- sink_fmt->width, sink_fmt->height,
- sink_fmt->pixelformat);
-
- /* The width, height and code must match. */
- vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
- if (source_fmt.format.width != sink_fmt->width
- || source_fmt.format.height != sink_fmt->height
- || vpix->code != source_fmt.format.code)
- return -EPIPE;
-
- /*
- * The field order must match, or the sink field order must be NONE
- * to support interlaced hardware connected to bridges that support
- * progressive formats only.
- */
- if (source_fmt.format.field != sink_fmt->field &&
- sink_fmt->field != V4L2_FIELD_NONE)
- return -EPIPE;
-
- return 0;
-}
-
static const struct media_entity_operations vimc_cap_mops = {
- .link_validate = vimc_cap_link_validate,
+ .link_validate = vimc_link_validate,
};
-static void vimc_cap_destroy(struct vimc_ent_device *ved)
+static void vimc_cap_comp_unbind(struct device *comp, struct device *master,
+ void *master_data)
{
+ struct vimc_ent_device *ved = dev_get_drvdata(comp);
struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
ved);
@@ -376,42 +392,35 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
}
-struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
- const char *const name,
- u16 num_pads,
- const unsigned long *pads_flag)
+static int vimc_cap_comp_bind(struct device *comp, struct device *master,
+ void *master_data)
{
+ struct v4l2_device *v4l2_dev = master_data;
+ struct vimc_platform_data *pdata = comp->platform_data;
const struct vimc_pix_map *vpix;
struct vimc_cap_device *vcap;
struct video_device *vdev;
struct vb2_queue *q;
int ret;
- /*
- * Check entity configuration params
- * NOTE: we only support a single sink pad
- */
- if (!name || num_pads != 1 || !pads_flag ||
- !(pads_flag[0] & MEDIA_PAD_FL_SINK))
- return ERR_PTR(-EINVAL);
-
/* Allocate the vimc_cap_device struct */
vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
if (!vcap)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
/* Allocate the pads */
- vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+ vcap->ved.pads =
+ vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});
if (IS_ERR(vcap->ved.pads)) {
ret = PTR_ERR(vcap->ved.pads);
goto err_free_vcap;
}
/* Initialize the media entity */
- vcap->vdev.entity.name = name;
+ vcap->vdev.entity.name = pdata->entity_name;
vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
ret = media_entity_pads_init(&vcap->vdev.entity,
- num_pads, vcap->ved.pads);
+ 1, vcap->ved.pads);
if (ret)
goto err_clean_pads;
@@ -432,9 +441,8 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
ret = vb2_queue_init(q);
if (ret) {
- dev_err(vcap->vdev.v4l2_dev->dev,
- "%s: vb2 queue init failed (err=%d)\n",
- vcap->vdev.name, ret);
+ dev_err(comp, "%s: vb2 queue init failed (err=%d)\n",
+ pdata->entity_name, ret);
goto err_clean_m_ent;
}
@@ -442,23 +450,19 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
INIT_LIST_HEAD(&vcap->buf_list);
spin_lock_init(&vcap->qlock);
- /* Set the frame format (this is hardcoded for now) */
- vcap->format.width = 640;
- vcap->format.height = 480;
- vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
- vcap->format.field = V4L2_FIELD_NONE;
- vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
-
+ /* Set default frame format */
+ vcap->format = fmt_default;
vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
-
vcap->format.bytesperline = vcap->format.width * vpix->bpp;
vcap->format.sizeimage = vcap->format.bytesperline *
vcap->format.height;
/* Fill the vimc_ent_device struct */
- vcap->ved.destroy = vimc_cap_destroy;
vcap->ved.ent = &vcap->vdev.entity;
vcap->ved.process_frame = vimc_cap_process_frame;
+ vcap->ved.vdev_get_format = vimc_cap_get_format;
+ dev_set_drvdata(comp, &vcap->ved);
+ vcap->dev = comp;
/* Initialize the video_device struct */
vdev = &vcap->vdev;
@@ -471,19 +475,18 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
vdev->queue = q;
vdev->v4l2_dev = v4l2_dev;
vdev->vfl_dir = VFL_DIR_RX;
- strlcpy(vdev->name, name, sizeof(vdev->name));
+ strlcpy(vdev->name, pdata->entity_name, sizeof(vdev->name));
video_set_drvdata(vdev, &vcap->ved);
/* Register the video_device with the v4l2 and the media framework */
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret) {
- dev_err(vcap->vdev.v4l2_dev->dev,
- "%s: video register failed (err=%d)\n",
+ dev_err(comp, "%s: video register failed (err=%d)\n",
vcap->vdev.name, ret);
goto err_release_queue;
}
- return &vcap->ved;
+ return 0;
err_release_queue:
vb2_queue_release(q);
@@ -494,5 +497,46 @@ err_clean_pads:
err_free_vcap:
kfree(vcap);
- return ERR_PTR(ret);
+ return ret;
+}
+
+static const struct component_ops vimc_cap_comp_ops = {
+ .bind = vimc_cap_comp_bind,
+ .unbind = vimc_cap_comp_unbind,
+};
+
+static int vimc_cap_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &vimc_cap_comp_ops);
}
+
+static int vimc_cap_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vimc_cap_comp_ops);
+
+ return 0;
+}
+
+static const struct platform_device_id vimc_cap_driver_ids[] = {
+ {
+ .name = VIMC_CAP_DRV_NAME,
+ },
+ { }
+};
+
+static struct platform_driver vimc_cap_pdrv = {
+ .probe = vimc_cap_probe,
+ .remove = vimc_cap_remove,
+ .id_table = vimc_cap_driver_ids,
+ .driver = {
+ .name = VIMC_CAP_DRV_NAME,
+ },
+};
+
+module_platform_driver(vimc_cap_pdrv);
+
+MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture");
+MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
deleted file mode 100644
index 581a813abdf1..000000000000
--- a/drivers/media/platform/vimc/vimc-capture.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * vimc-capture.h Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _VIMC_CAPTURE_H_
-#define _VIMC_CAPTURE_H_
-
-#include "vimc-core.h"
-
-struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
- const char *const name,
- u16 num_pads,
- const unsigned long *pads_flag);
-
-#endif
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
new file mode 100644
index 000000000000..9d63c84a9876
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-common.c
@@ -0,0 +1,473 @@
+/*
+ * vimc-common.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "vimc-common.h"
+
+/*
+ * NOTE: non-bayer formats need to come first (necessary for enum_mbus_code
+ * in the scaler)
+ */
+static const struct vimc_pix_map vimc_pix_map_list[] = {
+ /* TODO: add all missing formats */
+
+ /* RGB formats */
+ {
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .bpp = 3,
+ .bayer = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ .bpp = 3,
+ .bayer = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .pixelformat = V4L2_PIX_FMT_ARGB32,
+ .bpp = 4,
+ .bayer = false,
+ },
+
+ /* Bayer formats */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGBRG8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGRBG8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SRGGB8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SBGGR10,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SGBRG10,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SGRBG10,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pixelformat = V4L2_PIX_FMT_SRGGB10,
+ .bpp = 2,
+ .bayer = true,
+ },
+
+ /* 10bit raw bayer a-law compressed to 8 bits */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
+ .bpp = 1,
+ .bayer = true,
+ },
+
+ /* 10bit raw bayer DPCM compressed to 8 bits */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
+ .bpp = 1,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pixelformat = V4L2_PIX_FMT_SBGGR12,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pixelformat = V4L2_PIX_FMT_SGBRG12,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pixelformat = V4L2_PIX_FMT_SGRBG12,
+ .bpp = 2,
+ .bayer = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pixelformat = V4L2_PIX_FMT_SRGGB12,
+ .bpp = 2,
+ .bayer = true,
+ },
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i)
+{
+ if (i >= ARRAY_SIZE(vimc_pix_map_list))
+ return NULL;
+
+ return &vimc_pix_map_list[i];
+}
+EXPORT_SYMBOL_GPL(vimc_pix_map_by_index);
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+ if (vimc_pix_map_list[i].code == code)
+ return &vimc_pix_map_list[i];
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(vimc_pix_map_by_code);
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+ if (vimc_pix_map_list[i].pixelformat == pixelformat)
+ return &vimc_pix_map_list[i];
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat);
+
+int vimc_propagate_frame(struct media_pad *src, const void *frame)
+{
+ struct media_link *link;
+
+ if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+ return -EINVAL;
+
+ /* Send this frame to all sink pads that are direct linked */
+ list_for_each_entry(link, &src->entity->links, list) {
+ if (link->source == src &&
+ (link->flags & MEDIA_LNK_FL_ENABLED)) {
+ struct vimc_ent_device *ved = NULL;
+ struct media_entity *entity = link->sink->entity;
+
+ if (is_media_entity_v4l2_subdev(entity)) {
+ struct v4l2_subdev *sd =
+ container_of(entity, struct v4l2_subdev,
+ entity);
+ ved = v4l2_get_subdevdata(sd);
+ } else if (is_media_entity_v4l2_video_device(entity)) {
+ struct video_device *vdev =
+ container_of(entity,
+ struct video_device,
+ entity);
+ ved = video_get_drvdata(vdev);
+ }
+ if (ved && ved->process_frame)
+ ved->process_frame(ved, link->sink, frame);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vimc_propagate_frame);
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+ struct media_pad *pads;
+ unsigned int i;
+
+ /* Allocate memory for the pads */
+ pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+ if (!pads)
+ return ERR_PTR(-ENOMEM);
+
+ /* Initialize the pads */
+ for (i = 0; i < num_pads; i++) {
+ pads[i].index = i;
+ pads[i].flags = pads_flag[i];
+ }
+
+ return pads;
+}
+EXPORT_SYMBOL_GPL(vimc_pads_init);
+
+int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
+{
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ent->num_pads; i++) {
+ if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ continue;
+
+ /* Start the stream in the subdevice direct connected */
+ pad = media_entity_remote_pad(&ent->pads[i]);
+
+ if (!is_media_entity_v4l2_subdev(pad->entity))
+ return -EINVAL;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ ret = v4l2_subdev_call(sd, video, s_stream, enable);
+ if (ret && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream);
+
+static int vimc_get_mbus_format(struct media_pad *pad,
+ struct v4l2_subdev_format *fmt)
+{
+ if (is_media_entity_v4l2_subdev(pad->entity)) {
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(pad->entity);
+ int ret;
+
+ fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt->pad = pad->index;
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+ if (ret)
+ return ret;
+
+ } else if (is_media_entity_v4l2_video_device(pad->entity)) {
+ struct video_device *vdev = container_of(pad->entity,
+ struct video_device,
+ entity);
+ struct vimc_ent_device *ved = video_get_drvdata(vdev);
+ const struct vimc_pix_map *vpix;
+ struct v4l2_pix_format vdev_fmt;
+
+ if (!ved->vdev_get_format)
+ return -ENOIOCTLCMD;
+
+ ved->vdev_get_format(ved, &vdev_fmt);
+ vpix = vimc_pix_map_by_pixelformat(vdev_fmt.pixelformat);
+ v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, vpix->code);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vimc_link_validate(struct media_link *link)
+{
+ struct v4l2_subdev_format source_fmt, sink_fmt;
+ int ret;
+
+ ret = vimc_get_mbus_format(link->source, &source_fmt);
+ if (ret)
+ return ret;
+
+ ret = vimc_get_mbus_format(link->sink, &sink_fmt);
+ if (ret)
+ return ret;
+
+ pr_info("vimc link validate: "
+ "%s:src:%dx%d (0x%x, %d, %d, %d, %d) "
+ "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n",
+ /* src */
+ link->source->entity->name,
+ source_fmt.format.width, source_fmt.format.height,
+ source_fmt.format.code, source_fmt.format.colorspace,
+ source_fmt.format.quantization, source_fmt.format.xfer_func,
+ source_fmt.format.ycbcr_enc,
+ /* sink */
+ link->sink->entity->name,
+ sink_fmt.format.width, sink_fmt.format.height,
+ sink_fmt.format.code, sink_fmt.format.colorspace,
+ sink_fmt.format.quantization, sink_fmt.format.xfer_func,
+ sink_fmt.format.ycbcr_enc);
+
+ /* The width, height and code must match. */
+ if (source_fmt.format.width != sink_fmt.format.width
+ || source_fmt.format.height != sink_fmt.format.height
+ || source_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+
+ /*
+ * The field order must match, or the sink field order must be NONE
+ * to support interlaced hardware connected to bridges that support
+ * progressive formats only.
+ */
+ if (source_fmt.format.field != sink_fmt.format.field &&
+ sink_fmt.format.field != V4L2_FIELD_NONE)
+ return -EPIPE;
+
+ /*
+ * If colorspace is DEFAULT, then assume all the colorimetry is also
+ * DEFAULT, return 0 to skip comparing the other colorimetry parameters
+ */
+ if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT
+ || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT)
+ return 0;
+
+ /* Colorspace must match. */
+ if (source_fmt.format.colorspace != sink_fmt.format.colorspace)
+ return -EPIPE;
+
+ /* Colorimetry must match if they are not set to DEFAULT */
+ if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
+ && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
+ && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc)
+ return -EPIPE;
+
+ if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
+ && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
+ && source_fmt.format.quantization != sink_fmt.format.quantization)
+ return -EPIPE;
+
+ if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
+ && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
+ && source_fmt.format.xfer_func != sink_fmt.format.xfer_func)
+ return -EPIPE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vimc_link_validate);
+
+static const struct media_entity_operations vimc_ent_sd_mops = {
+ .link_validate = vimc_link_validate,
+};
+
+int vimc_ent_sd_register(struct vimc_ent_device *ved,
+ struct v4l2_subdev *sd,
+ struct v4l2_device *v4l2_dev,
+ const char *const name,
+ u32 function,
+ u16 num_pads,
+ const unsigned long *pads_flag,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ int ret;
+
+ /* Allocate the pads */
+ ved->pads = vimc_pads_init(num_pads, pads_flag);
+ if (IS_ERR(ved->pads))
+ return PTR_ERR(ved->pads);
+
+ /* Fill the vimc_ent_device struct */
+ ved->ent = &sd->entity;
+
+ /* Initialize the subdev */
+ v4l2_subdev_init(sd, sd_ops);
+ sd->entity.function = function;
+ sd->entity.ops = &vimc_ent_sd_mops;
+ sd->owner = THIS_MODULE;
+ strlcpy(sd->name, name, sizeof(sd->name));
+ v4l2_set_subdevdata(sd, ved);
+
+ /* Expose this subdev to user space */
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ /* Initialize the media entity */
+ ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads);
+ if (ret)
+ goto err_clean_pads;
+
+ /* Register the subdev with the v4l2 and the media framework */
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret) {
+ dev_err(v4l2_dev->dev,
+ "%s: subdev register failed (err=%d)\n",
+ name, ret);
+ goto err_clean_m_ent;
+ }
+
+ return 0;
+
+err_clean_m_ent:
+ media_entity_cleanup(&sd->entity);
+err_clean_pads:
+ vimc_pads_cleanup(ved->pads);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vimc_ent_sd_register);
+
+void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd)
+{
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(ved->ent);
+ vimc_pads_cleanup(ved->pads);
+}
+EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Common");
+MODULE_AUTHOR("Helen Koike <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
new file mode 100644
index 000000000000..dca528a316e7
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -0,0 +1,229 @@
+/*
+ * vimc-common.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_COMMON_H_
+#define _VIMC_COMMON_H_
+
+#include <linux/slab.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#define VIMC_FRAME_MAX_WIDTH 4096
+#define VIMC_FRAME_MAX_HEIGHT 2160
+#define VIMC_FRAME_MIN_WIDTH 16
+#define VIMC_FRAME_MIN_HEIGHT 16
+
+#define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp)
+
+/**
+ * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
+ *
+ * @fmt: the pointer to struct v4l2_pix_format or
+ * struct v4l2_mbus_framefmt
+ *
+ * Entities must check if colorimetry given by the userspace is valid, if not
+ * then set them as DEFAULT
+ */
+#define vimc_colorimetry_clamp(fmt) \
+do { \
+ if ((fmt)->colorspace == V4L2_COLORSPACE_DEFAULT \
+ || (fmt)->colorspace > V4L2_COLORSPACE_DCI_P3) { \
+ (fmt)->colorspace = V4L2_COLORSPACE_DEFAULT; \
+ (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \
+ (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \
+ (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \
+ } \
+ if ((fmt)->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) \
+ (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \
+ if ((fmt)->quantization > V4L2_QUANTIZATION_LIM_RANGE) \
+ (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \
+ if ((fmt)->xfer_func > V4L2_XFER_FUNC_SMPTE2084) \
+ (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \
+} while (0)
+
+/**
+ * struct vimc_platform_data - platform data to components
+ *
+ * @entity_name: The name of the entity to be created
+ *
+ * Board setup code will often provide additional information using the device's
+ * platform_data field to hold additional information.
+ * When injecting a new platform_device in the component system the core needs
+ * to provide to the corresponding submodules the name of the entity that should
+ * be used when registering the subdevice in the Media Controller system.
+ */
+struct vimc_platform_data {
+ char entity_name[32];
+};
+
+/**
+ * struct vimc_pix_map - maps media bus code with v4l2 pixel format
+ *
+ * @code: media bus format code defined by MEDIA_BUS_FMT_* macros
+ * @bbp: number of bytes each pixel occupies
+ * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros
+ *
+ * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
+ * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+ unsigned int code;
+ unsigned int bpp;
+ u32 pixelformat;
+ bool bayer;
+};
+
+/**
+ * struct vimc_ent_device - core struct that represents a node in the topology
+ *
+ * @ent: the pointer to struct media_entity for the node
+ * @pads: the list of pads of the node
+ * @process_frame: callback send a frame to that node
+ * @vdev_get_format: callback that returns the current format a pad, used
+ * only when is_media_entity_v4l2_video_device(ent) returns
+ * true
+ *
+ * Each node of the topology must create a vimc_ent_device struct. Depending on
+ * the node it will be of an instance of v4l2_subdev or video_device struct
+ * where both contains a struct media_entity.
+ * Those structures should embedded the vimc_ent_device struct through
+ * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
+ * vimc_ent_device struct to be retrieved from the corresponding struct
+ * media_entity
+ */
+struct vimc_ent_device {
+ struct media_entity *ent;
+ struct media_pad *pads;
+ void (*process_frame)(struct vimc_ent_device *ved,
+ struct media_pad *sink, const void *frame);
+ void (*vdev_get_format)(struct vimc_ent_device *ved,
+ struct v4l2_pix_format *fmt);
+};
+
+/**
+ * vimc_propagate_frame - propagate a frame through the topology
+ *
+ * @src: the source pad where the frame is being originated
+ * @frame: the frame to be propagated
+ *
+ * This function will call the process_frame callback from the vimc_ent_device
+ * struct of the nodes directly connected to the @src pad
+ */
+int vimc_propagate_frame(struct media_pad *src, const void *frame);
+
+/**
+ * vimc_pads_init - initialize pads
+ *
+ * @num_pads: number of pads to initialize
+ * @pads_flags: flags to use in each pad
+ *
+ * Helper functions to allocate/initialize pads
+ */
+struct media_pad *vimc_pads_init(u16 num_pads,
+ const unsigned long *pads_flag);
+
+/**
+ * vimc_pads_cleanup - free pads
+ *
+ * @pads: pointer to the pads
+ *
+ * Helper function to free the pads initialized with vimc_pads_init
+ */
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+ kfree(pads);
+}
+
+/**
+ * vimc_pipeline_s_stream - start stream through the pipeline
+ *
+ * @ent: the pointer to struct media_entity for the node
+ * @enable: 1 to start the stream and 0 to stop
+ *
+ * Helper function to call the s_stream of the subdevices connected
+ * in all the sink pads of the entity
+ */
+int vimc_pipeline_s_stream(struct media_entity *ent, int enable);
+
+/**
+ * vimc_pix_map_by_index - get vimc_pix_map struct by its index
+ *
+ * @i: index of the vimc_pix_map struct in vimc_pix_map_list
+ */
+const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i);
+
+/**
+ * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
+ *
+ * @code: media bus format code defined by MEDIA_BUS_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+/**
+ * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
+ *
+ * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+/**
+ * vimc_ent_sd_register - initialize and register a subdev node
+ *
+ * @ved: the vimc_ent_device struct to be initialize
+ * @sd: the v4l2_subdev struct to be initialize and registered
+ * @v4l2_dev: the v4l2 device to register the v4l2_subdev
+ * @name: name of the sub-device. Please notice that the name must be
+ * unique.
+ * @function: media entity function defined by MEDIA_ENT_F_* macros
+ * @num_pads: number of pads to initialize
+ * @pads_flag: flags to use in each pad
+ * @sd_ops: pointer to &struct v4l2_subdev_ops.
+ *
+ * Helper function initialize and register the struct vimc_ent_device and struct
+ * v4l2_subdev which represents a subdev node in the topology
+ */
+int vimc_ent_sd_register(struct vimc_ent_device *ved,
+ struct v4l2_subdev *sd,
+ struct v4l2_device *v4l2_dev,
+ const char *const name,
+ u32 function,
+ u16 num_pads,
+ const unsigned long *pads_flag,
+ const struct v4l2_subdev_ops *sd_ops);
+
+/**
+ * vimc_ent_sd_unregister - cleanup and unregister a subdev node
+ *
+ * @ved: the vimc_ent_device struct to be cleaned up
+ * @sd: the v4l2_subdev struct to be unregistered
+ *
+ * Helper function cleanup and unregister the struct vimc_ent_device and struct
+ * v4l2_subdev which represents a subdev node in the topology
+ */
+void vimc_ent_sd_unregister(struct vimc_ent_device *ved,
+ struct v4l2_subdev *sd);
+
+/**
+ * vimc_link_validate - validates a media link
+ *
+ * @link: pointer to &struct media_link
+ *
+ * This function calls validates if a media link is valid for streaming.
+ */
+int vimc_link_validate(struct media_link *link);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index bc107da8fbd5..51c0eee61ca6 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -15,15 +15,14 @@
*
*/
+#include <linux/component.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
-#include "vimc-capture.h"
-#include "vimc-core.h"
-#include "vimc-sensor.h"
+#include "vimc-common.h"
#define VIMC_PDEV_NAME "vimc"
#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
@@ -37,10 +36,10 @@
}
struct vimc_device {
- /*
- * The pipeline configuration
- * (filled before calling vimc_device_register)
- */
+ /* The platform device */
+ struct platform_device pdev;
+
+ /* The pipeline configuration */
const struct vimc_pipeline_config *pipe_cfg;
/* The Associated media_device parent */
@@ -49,43 +48,14 @@ struct vimc_device {
/* Internal v4l2 parent device*/
struct v4l2_device v4l2_dev;
- /* Internal topology */
- struct vimc_ent_device **ved;
-};
-
-/**
- * enum vimc_ent_node - Select the functionality of a node in the topology
- * @VIMC_ENT_NODE_SENSOR: A node of type SENSOR simulates a camera sensor
- * generating internal images in bayer format and
- * propagating those images through the pipeline
- * @VIMC_ENT_NODE_CAPTURE: A node of type CAPTURE is a v4l2 video_device
- * that exposes the received image from the
- * pipeline to the user space
- * @VIMC_ENT_NODE_INPUT: A node of type INPUT is a v4l2 video_device that
- * receives images from the user space and
- * propagates them through the pipeline
- * @VIMC_ENT_NODE_DEBAYER: A node type DEBAYER expects to receive a frame
- * in bayer format converts it to RGB
- * @VIMC_ENT_NODE_SCALER: A node of type SCALER scales the received image
- * by a given multiplier
- *
- * This enum is used in the entity configuration struct to allow the definition
- * of a custom topology specifying the role of each node on it.
- */
-enum vimc_ent_node {
- VIMC_ENT_NODE_SENSOR,
- VIMC_ENT_NODE_CAPTURE,
- VIMC_ENT_NODE_INPUT,
- VIMC_ENT_NODE_DEBAYER,
- VIMC_ENT_NODE_SCALER,
+ /* Subdevices */
+ struct platform_device **subdevs;
};
/* Structure which describes individual configuration for each entity */
struct vimc_ent_config {
const char *name;
- size_t pads_qty;
- const unsigned long *pads_flag;
- enum vimc_ent_node node;
+ const char *drv;
};
/* Structure which describes links between entities */
@@ -112,60 +82,40 @@ struct vimc_pipeline_config {
static const struct vimc_ent_config ent_config[] = {
{
.name = "Sensor A",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_SENSOR,
+ .drv = "vimc-sensor",
},
{
.name = "Sensor B",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_SENSOR,
+ .drv = "vimc-sensor",
},
{
.name = "Debayer A",
- .pads_qty = 2,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
- MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_DEBAYER,
+ .drv = "vimc-debayer",
},
{
.name = "Debayer B",
- .pads_qty = 2,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
- MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_DEBAYER,
+ .drv = "vimc-debayer",
},
{
.name = "Raw Capture 0",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
- .node = VIMC_ENT_NODE_CAPTURE,
+ .drv = "vimc-capture",
},
{
.name = "Raw Capture 1",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
- .node = VIMC_ENT_NODE_CAPTURE,
+ .drv = "vimc-capture",
},
{
.name = "RGB/YUV Input",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_INPUT,
+ /* TODO: change this to vimc-input when it is implemented */
+ .drv = "vimc-sensor",
},
{
.name = "Scaler",
- .pads_qty = 2,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
- MEDIA_PAD_FL_SOURCE},
- .node = VIMC_ENT_NODE_SCALER,
+ .drv = "vimc-scaler",
},
{
.name = "RGB/YUV Capture",
- .pads_qty = 1,
- .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
- .node = VIMC_ENT_NODE_CAPTURE,
+ .drv = "vimc-capture",
},
};
@@ -197,314 +147,40 @@ static const struct vimc_pipeline_config pipe_cfg = {
/* -------------------------------------------------------------------------- */
-static const struct vimc_pix_map vimc_pix_map_list[] = {
- /* TODO: add all missing formats */
-
- /* RGB formats */
- {
- .code = MEDIA_BUS_FMT_BGR888_1X24,
- .pixelformat = V4L2_PIX_FMT_BGR24,
- .bpp = 3,
- },
- {
- .code = MEDIA_BUS_FMT_RGB888_1X24,
- .pixelformat = V4L2_PIX_FMT_RGB24,
- .bpp = 3,
- },
- {
- .code = MEDIA_BUS_FMT_ARGB8888_1X32,
- .pixelformat = V4L2_PIX_FMT_ARGB32,
- .bpp = 4,
- },
-
- /* Bayer formats */
- {
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .pixelformat = V4L2_PIX_FMT_SBGGR10,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGBRG10,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .pixelformat = V4L2_PIX_FMT_SGRBG10,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .pixelformat = V4L2_PIX_FMT_SRGGB10,
- .bpp = 2,
- },
-
- /* 10bit raw bayer a-law compressed to 8 bits */
- {
- .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
- .bpp = 1,
- },
-
- /* 10bit raw bayer DPCM compressed to 8 bits */
- {
- .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
- .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
- .bpp = 1,
- },
- {
- .code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .pixelformat = V4L2_PIX_FMT_SBGGR12,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .pixelformat = V4L2_PIX_FMT_SGBRG12,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .pixelformat = V4L2_PIX_FMT_SGRBG12,
- .bpp = 2,
- },
- {
- .code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .pixelformat = V4L2_PIX_FMT_SRGGB12,
- .bpp = 2,
- },
-};
-
-const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
- if (vimc_pix_map_list[i].code == code)
- return &vimc_pix_map_list[i];
- }
- return NULL;
-}
-
-const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
- if (vimc_pix_map_list[i].pixelformat == pixelformat)
- return &vimc_pix_map_list[i];
- }
- return NULL;
-}
-
-int vimc_propagate_frame(struct media_pad *src, const void *frame)
-{
- struct media_link *link;
-
- if (!(src->flags & MEDIA_PAD_FL_SOURCE))
- return -EINVAL;
-
- /* Send this frame to all sink pads that are direct linked */
- list_for_each_entry(link, &src->entity->links, list) {
- if (link->source == src &&
- (link->flags & MEDIA_LNK_FL_ENABLED)) {
- struct vimc_ent_device *ved = NULL;
- struct media_entity *entity = link->sink->entity;
-
- if (is_media_entity_v4l2_subdev(entity)) {
- struct v4l2_subdev *sd =
- container_of(entity, struct v4l2_subdev,
- entity);
- ved = v4l2_get_subdevdata(sd);
- } else if (is_media_entity_v4l2_video_device(entity)) {
- struct video_device *vdev =
- container_of(entity,
- struct video_device,
- entity);
- ved = video_get_drvdata(vdev);
- }
- if (ved && ved->process_frame)
- ved->process_frame(ved, link->sink, frame);
- }
- }
-
- return 0;
-}
-
-static void vimc_device_unregister(struct vimc_device *vimc)
-{
- unsigned int i;
-
- media_device_unregister(&vimc->mdev);
- /* Cleanup (only initialized) entities */
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
- if (vimc->ved[i] && vimc->ved[i]->destroy)
- vimc->ved[i]->destroy(vimc->ved[i]);
-
- vimc->ved[i] = NULL;
- }
- v4l2_device_unregister(&vimc->v4l2_dev);
- media_device_cleanup(&vimc->mdev);
-}
-
-/* Helper function to allocate and initialize pads */
-struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+static int vimc_create_links(struct vimc_device *vimc)
{
- struct media_pad *pads;
unsigned int i;
-
- /* Allocate memory for the pads */
- pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
- if (!pads)
- return ERR_PTR(-ENOMEM);
-
- /* Initialize the pads */
- for (i = 0; i < num_pads; i++) {
- pads[i].index = i;
- pads[i].flags = pads_flag[i];
- }
-
- return pads;
-}
-
-/*
- * TODO: remove this function when all the
- * entities specific code are implemented
- */
-static void vimc_raw_destroy(struct vimc_ent_device *ved)
-{
- media_device_unregister_entity(ved->ent);
-
- media_entity_cleanup(ved->ent);
-
- vimc_pads_cleanup(ved->pads);
-
- kfree(ved->ent);
-
- kfree(ved);
-}
-
-/*
- * TODO: remove this function when all the
- * entities specific code are implemented
- */
-static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
- const char *const name,
- u16 num_pads,
- const unsigned long *pads_flag)
-{
- struct vimc_ent_device *ved;
int ret;
- /* Allocate the main ved struct */
- ved = kzalloc(sizeof(*ved), GFP_KERNEL);
- if (!ved)
- return ERR_PTR(-ENOMEM);
-
- /* Allocate the media entity */
- ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
- if (!ved->ent) {
- ret = -ENOMEM;
- goto err_free_ved;
- }
-
- /* Allocate the pads */
- ved->pads = vimc_pads_init(num_pads, pads_flag);
- if (IS_ERR(ved->pads)) {
- ret = PTR_ERR(ved->pads);
- goto err_free_ent;
+ /* Initialize the links between entities */
+ for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
+ const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
+ /*
+ * TODO: Check another way of retrieving ved struct without
+ * relying on platform_get_drvdata
+ */
+ struct vimc_ent_device *ved_src =
+ platform_get_drvdata(vimc->subdevs[link->src_ent]);
+ struct vimc_ent_device *ved_sink =
+ platform_get_drvdata(vimc->subdevs[link->sink_ent]);
+
+ ret = media_create_pad_link(ved_src->ent, link->src_pad,
+ ved_sink->ent, link->sink_pad,
+ link->flags);
+ if (ret)
+ return ret;
}
- /* Initialize the media entity */
- ved->ent->name = name;
- ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
- ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
- if (ret)
- goto err_cleanup_pads;
-
- /* Register the media entity */
- ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
- if (ret)
- goto err_cleanup_entity;
-
- /* Fill out the destroy function and return */
- ved->destroy = vimc_raw_destroy;
- return ved;
-
-err_cleanup_entity:
- media_entity_cleanup(ved->ent);
-err_cleanup_pads:
- vimc_pads_cleanup(ved->pads);
-err_free_ent:
- kfree(ved->ent);
-err_free_ved:
- kfree(ved);
-
- return ERR_PTR(ret);
+ return 0;
}
-static int vimc_device_register(struct vimc_device *vimc)
+static int vimc_comp_bind(struct device *master)
{
- unsigned int i;
+ struct vimc_device *vimc = container_of(to_platform_device(master),
+ struct vimc_device, pdev);
int ret;
- /* Allocate memory for the vimc_ent_devices pointers */
- vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
- sizeof(*vimc->ved), GFP_KERNEL);
- if (!vimc->ved)
- return -ENOMEM;
-
- /* Link the media device within the v4l2_device */
- vimc->v4l2_dev.mdev = &vimc->mdev;
+ dev_dbg(master, "bind");
/* Register the v4l2 struct */
ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
@@ -514,66 +190,22 @@ static int vimc_device_register(struct vimc_device *vimc)
return ret;
}
- /* Initialize entities */
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
- struct vimc_ent_device *(*create_func)(struct v4l2_device *,
- const char *const,
- u16,
- const unsigned long *);
-
- /* Register the specific node */
- switch (vimc->pipe_cfg->ents[i].node) {
- case VIMC_ENT_NODE_SENSOR:
- create_func = vimc_sen_create;
- break;
-
- case VIMC_ENT_NODE_CAPTURE:
- create_func = vimc_cap_create;
- break;
-
- /* TODO: Instantiate the specific topology node */
- case VIMC_ENT_NODE_INPUT:
- case VIMC_ENT_NODE_DEBAYER:
- case VIMC_ENT_NODE_SCALER:
- default:
- /*
- * TODO: remove this when all the entities specific
- * code are implemented
- */
- create_func = vimc_raw_create;
- break;
- }
-
- vimc->ved[i] = create_func(&vimc->v4l2_dev,
- vimc->pipe_cfg->ents[i].name,
- vimc->pipe_cfg->ents[i].pads_qty,
- vimc->pipe_cfg->ents[i].pads_flag);
- if (IS_ERR(vimc->ved[i])) {
- ret = PTR_ERR(vimc->ved[i]);
- vimc->ved[i] = NULL;
- goto err;
- }
- }
-
- /* Initialize the links between entities */
- for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
- const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
+ /* Bind subdevices */
+ ret = component_bind_all(master, &vimc->v4l2_dev);
+ if (ret)
+ goto err_v4l2_unregister;
- ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
- link->src_pad,
- vimc->ved[link->sink_ent]->ent,
- link->sink_pad,
- link->flags);
- if (ret)
- goto err;
- }
+ /* Initialize links */
+ ret = vimc_create_links(vimc);
+ if (ret)
+ goto err_comp_unbind_all;
/* Register the media device */
ret = media_device_register(&vimc->mdev);
if (ret) {
dev_err(vimc->mdev.dev,
"media device register failed (err=%d)\n", ret);
- return ret;
+ goto err_comp_unbind_all;
}
/* Expose all subdev's nodes*/
@@ -582,32 +214,106 @@ static int vimc_device_register(struct vimc_device *vimc)
dev_err(vimc->mdev.dev,
"vimc subdev nodes registration failed (err=%d)\n",
ret);
- goto err;
+ goto err_mdev_unregister;
}
return 0;
-err:
- /* Destroy the so far created topology */
- vimc_device_unregister(vimc);
+err_mdev_unregister:
+ media_device_unregister(&vimc->mdev);
+err_comp_unbind_all:
+ component_unbind_all(master, NULL);
+err_v4l2_unregister:
+ v4l2_device_unregister(&vimc->v4l2_dev);
return ret;
}
+static void vimc_comp_unbind(struct device *master)
+{
+ struct vimc_device *vimc = container_of(to_platform_device(master),
+ struct vimc_device, pdev);
+
+ dev_dbg(master, "unbind");
+
+ media_device_unregister(&vimc->mdev);
+ component_unbind_all(master, NULL);
+ v4l2_device_unregister(&vimc->v4l2_dev);
+}
+
+static int vimc_comp_compare(struct device *comp, void *data)
+{
+ const struct platform_device *pdev = to_platform_device(comp);
+ const char *name = data;
+
+ return !strcmp(pdev->dev.platform_data, name);
+}
+
+static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
+{
+ struct component_match *match = NULL;
+ struct vimc_platform_data pdata;
+ int i;
+
+ for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+ dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
+ vimc->pipe_cfg->ents[i].drv);
+
+ strlcpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
+ sizeof(pdata.entity_name));
+
+ vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
+ vimc->pipe_cfg->ents[i].drv,
+ PLATFORM_DEVID_AUTO,
+ &pdata,
+ sizeof(pdata));
+ if (!vimc->subdevs[i]) {
+ while (--i >= 0)
+ platform_device_unregister(vimc->subdevs[i]);
+
+ return ERR_PTR(-ENOMEM);
+ }
+
+ component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
+ (void *)vimc->pipe_cfg->ents[i].name);
+ }
+
+ return match;
+}
+
+static void vimc_rm_subdevs(struct vimc_device *vimc)
+{
+ unsigned int i;
+
+ for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
+ platform_device_unregister(vimc->subdevs[i]);
+}
+
+static const struct component_master_ops vimc_comp_ops = {
+ .bind = vimc_comp_bind,
+ .unbind = vimc_comp_unbind,
+};
+
static int vimc_probe(struct platform_device *pdev)
{
- struct vimc_device *vimc;
+ struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
+ struct component_match *match = NULL;
int ret;
- /* Prepare the vimc topology structure */
+ dev_dbg(&pdev->dev, "probe");
- /* Allocate memory for the vimc structure */
- vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
- if (!vimc)
+ /* Create platform_device for each entity in the topology*/
+ vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
+ sizeof(*vimc->subdevs), GFP_KERNEL);
+ if (!vimc->subdevs)
return -ENOMEM;
- /* Set the pipeline configuration struct */
- vimc->pipe_cfg = &pipe_cfg;
+ match = vimc_add_subdevs(vimc);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+
+ /* Link the media device within the v4l2_device */
+ vimc->v4l2_dev.mdev = &vimc->mdev;
/* Initialize media device */
strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
@@ -615,28 +321,27 @@ static int vimc_probe(struct platform_device *pdev)
vimc->mdev.dev = &pdev->dev;
media_device_init(&vimc->mdev);
- /* Create vimc topology */
- ret = vimc_device_register(vimc);
+ /* Add self to the component system */
+ ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
+ match);
if (ret) {
- dev_err(vimc->mdev.dev,
- "vimc device registration failed (err=%d)\n", ret);
+ media_device_cleanup(&vimc->mdev);
+ vimc_rm_subdevs(vimc);
kfree(vimc);
return ret;
}
- /* Link the topology object with the platform device object */
- platform_set_drvdata(pdev, vimc);
-
return 0;
}
static int vimc_remove(struct platform_device *pdev)
{
- struct vimc_device *vimc = platform_get_drvdata(pdev);
+ struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
+
+ dev_dbg(&pdev->dev, "remove");
- /* Destroy all the topology */
- vimc_device_unregister(vimc);
- kfree(vimc);
+ component_master_del(&pdev->dev, &vimc_comp_ops);
+ vimc_rm_subdevs(vimc);
return 0;
}
@@ -645,9 +350,12 @@ static void vimc_dev_release(struct device *dev)
{
}
-static struct platform_device vimc_pdev = {
- .name = VIMC_PDEV_NAME,
- .dev.release = vimc_dev_release,
+static struct vimc_device vimc_dev = {
+ .pipe_cfg = &pipe_cfg,
+ .pdev = {
+ .name = VIMC_PDEV_NAME,
+ .dev.release = vimc_dev_release,
+ }
};
static struct platform_driver vimc_pdrv = {
@@ -662,29 +370,29 @@ static int __init vimc_init(void)
{
int ret;
- ret = platform_device_register(&vimc_pdev);
+ ret = platform_device_register(&vimc_dev.pdev);
if (ret) {
- dev_err(&vimc_pdev.dev,
+ dev_err(&vimc_dev.pdev.dev,
"platform device registration failed (err=%d)\n", ret);
return ret;
}
ret = platform_driver_register(&vimc_pdrv);
if (ret) {
- dev_err(&vimc_pdev.dev,
+ dev_err(&vimc_dev.pdev.dev,
"platform driver registration failed (err=%d)\n", ret);
-
- platform_device_unregister(&vimc_pdev);
+ platform_driver_unregister(&vimc_pdrv);
+ return ret;
}
- return ret;
+ return 0;
}
static void __exit vimc_exit(void)
{
platform_driver_unregister(&vimc_pdrv);
- platform_device_unregister(&vimc_pdev);
+ platform_device_unregister(&vimc_dev.pdev);
}
module_init(vimc_init);
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
deleted file mode 100644
index 4525d23211ca..000000000000
--- a/drivers/media/platform/vimc/vimc-core.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * vimc-core.h Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _VIMC_CORE_H_
-#define _VIMC_CORE_H_
-
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-
-/**
- * struct vimc_pix_map - maps media bus code with v4l2 pixel format
- *
- * @code: media bus format code defined by MEDIA_BUS_FMT_* macros
- * @bbp: number of bytes each pixel occupies
- * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros
- *
- * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
- * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
- */
-struct vimc_pix_map {
- unsigned int code;
- unsigned int bpp;
- u32 pixelformat;
-};
-
-/**
- * struct vimc_ent_device - core struct that represents a node in the topology
- *
- * @ent: the pointer to struct media_entity for the node
- * @pads: the list of pads of the node
- * @destroy: callback to destroy the node
- * @process_frame: callback send a frame to that node
- *
- * Each node of the topology must create a vimc_ent_device struct. Depending on
- * the node it will be of an instance of v4l2_subdev or video_device struct
- * where both contains a struct media_entity.
- * Those structures should embedded the vimc_ent_device struct through
- * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
- * vimc_ent_device struct to be retrieved from the corresponding struct
- * media_entity
- */
-struct vimc_ent_device {
- struct media_entity *ent;
- struct media_pad *pads;
- void (*destroy)(struct vimc_ent_device *);
- void (*process_frame)(struct vimc_ent_device *ved,
- struct media_pad *sink, const void *frame);
-};
-
-/**
- * vimc_propagate_frame - propagate a frame through the topology
- *
- * @src: the source pad where the frame is being originated
- * @frame: the frame to be propagated
- *
- * This function will call the process_frame callback from the vimc_ent_device
- * struct of the nodes directly connected to the @src pad
- */
-int vimc_propagate_frame(struct media_pad *src, const void *frame);
-
-/**
- * vimc_pads_init - initialize pads
- *
- * @num_pads: number of pads to initialize
- * @pads_flags: flags to use in each pad
- *
- * Helper functions to allocate/initialize pads
- */
-struct media_pad *vimc_pads_init(u16 num_pads,
- const unsigned long *pads_flag);
-
-/**
- * vimc_pads_cleanup - free pads
- *
- * @pads: pointer to the pads
- *
- * Helper function to free the pads initialized with vimc_pads_init
- */
-static inline void vimc_pads_cleanup(struct media_pad *pads)
-{
- kfree(pads);
-}
-
-/**
- * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
- *
- * @code: media bus format code defined by MEDIA_BUS_FMT_* macros
- */
-const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
-
-/**
- * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
- *
- * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros
- */
-const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
-
-#endif
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
new file mode 100644
index 000000000000..033a131f67af
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -0,0 +1,602 @@
+/*
+ * vimc-debayer.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-common.h"
+
+#define VIMC_DEB_DRV_NAME "vimc-debayer"
+
+static unsigned int deb_mean_win_size = 3;
+module_param(deb_mean_win_size, uint, 0000);
+MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n"
+ "NOTE: the window size need to be an odd number, as the main pixel "
+ "stays in the center of the window, otherwise the next odd number "
+ "is considered");
+
+#define IS_SINK(pad) (!pad)
+#define IS_SRC(pad) (pad)
+
+enum vimc_deb_rgb_colors {
+ VIMC_DEB_RED = 0,
+ VIMC_DEB_GREEN = 1,
+ VIMC_DEB_BLUE = 2,
+};
+
+struct vimc_deb_pix_map {
+ u32 code;
+ enum vimc_deb_rgb_colors order[2][2];
+};
+
+struct vimc_deb_device {
+ struct vimc_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+ /* The active format */
+ struct v4l2_mbus_framefmt sink_fmt;
+ u32 src_code;
+ void (*set_rgb_src)(struct vimc_deb_device *vdeb, unsigned int lin,
+ unsigned int col, unsigned int rgb[3]);
+ /* Values calculated when the stream starts */
+ u8 *src_frame;
+ const struct vimc_deb_pix_map *sink_pix_map;
+ unsigned int sink_bpp;
+};
+
+static const struct v4l2_mbus_framefmt sink_fmt_default = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+};
+
+static const struct vimc_deb_pix_map vimc_deb_pix_map_list[] = {
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_RED } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
+ { VIMC_DEB_RED, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
+ { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_RED } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
+ { VIMC_DEB_RED, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
+ { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_RED } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE },
+ { VIMC_DEB_RED, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED },
+ { VIMC_DEB_BLUE, VIMC_DEB_GREEN } }
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN },
+ { VIMC_DEB_GREEN, VIMC_DEB_BLUE } }
+ },
+};
+
+static const struct vimc_deb_pix_map *vimc_deb_pix_map_by_code(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vimc_deb_pix_map_list); i++)
+ if (vimc_deb_pix_map_list[i].code == code)
+ return &vimc_deb_pix_map_list[i];
+
+ return NULL;
+}
+
+static int vimc_deb_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int i;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+ *mf = sink_fmt_default;
+
+ for (i = 1; i < sd->entity.num_pads; i++) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, i);
+ *mf = sink_fmt_default;
+ mf->code = vdeb->src_code;
+ }
+
+ return 0;
+}
+
+static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* We only support one format for source pads */
+ if (IS_SRC(code->pad)) {
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+
+ if (code->index)
+ return -EINVAL;
+
+ code->code = vdeb->src_code;
+ } else {
+ if (code->index >= ARRAY_SIZE(vimc_deb_pix_map_list))
+ return -EINVAL;
+
+ code->code = vimc_deb_pix_map_list[code->index].code;
+ }
+
+ return 0;
+}
+
+static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+
+ if (fse->index)
+ return -EINVAL;
+
+ if (IS_SINK(fse->pad)) {
+ const struct vimc_deb_pix_map *vpix =
+ vimc_deb_pix_map_by_code(fse->code);
+
+ if (!vpix)
+ return -EINVAL;
+ } else if (fse->code != vdeb->src_code) {
+ return -EINVAL;
+ }
+
+ fse->min_width = VIMC_FRAME_MIN_WIDTH;
+ fse->max_width = VIMC_FRAME_MAX_WIDTH;
+ fse->min_height = VIMC_FRAME_MIN_HEIGHT;
+ fse->max_height = VIMC_FRAME_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int vimc_deb_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+
+ /* Get the current sink format */
+ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
+ *v4l2_subdev_get_try_format(sd, cfg, 0) :
+ vdeb->sink_fmt;
+
+ /* Set the right code for the source pad */
+ if (IS_SRC(fmt->pad))
+ fmt->format.code = vdeb->src_code;
+
+ return 0;
+}
+
+static void vimc_deb_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ const struct vimc_deb_pix_map *vpix;
+
+ /* Don't accept a code that is not on the debayer table */
+ vpix = vimc_deb_pix_map_by_code(fmt->code);
+ if (!vpix)
+ fmt->code = sink_fmt_default.code;
+
+ fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
+ VIMC_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
+ VIMC_FRAME_MAX_HEIGHT) & ~1;
+
+ if (fmt->field == V4L2_FIELD_ANY)
+ fmt->field = sink_fmt_default.field;
+
+ vimc_colorimetry_clamp(fmt);
+}
+
+static int vimc_deb_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ /* Do not change the format while stream is on */
+ if (vdeb->src_frame)
+ return -EBUSY;
+
+ sink_fmt = &vdeb->sink_fmt;
+ } else {
+ sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+ }
+
+ /*
+ * Do not change the format of the source pad,
+ * it is propagated from the sink
+ */
+ if (IS_SRC(fmt->pad)) {
+ fmt->format = *sink_fmt;
+ /* TODO: Add support for other formats */
+ fmt->format.code = vdeb->src_code;
+ } else {
+ /* Set the new format in the sink pad */
+ vimc_deb_adjust_sink_fmt(&fmt->format);
+
+ dev_dbg(vdeb->dev, "%s: sink format update: "
+ "old:%dx%d (0x%x, %d, %d, %d, %d) "
+ "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdeb->sd.name,
+ /* old */
+ sink_fmt->width, sink_fmt->height, sink_fmt->code,
+ sink_fmt->colorspace, sink_fmt->quantization,
+ sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
+ /* new */
+ fmt->format.width, fmt->format.height, fmt->format.code,
+ fmt->format.colorspace, fmt->format.quantization,
+ fmt->format.xfer_func, fmt->format.ycbcr_enc);
+
+ *sink_fmt = fmt->format;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_deb_pad_ops = {
+ .init_cfg = vimc_deb_init_cfg,
+ .enum_mbus_code = vimc_deb_enum_mbus_code,
+ .enum_frame_size = vimc_deb_enum_frame_size,
+ .get_fmt = vimc_deb_get_fmt,
+ .set_fmt = vimc_deb_set_fmt,
+};
+
+static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb,
+ unsigned int lin,
+ unsigned int col,
+ unsigned int rgb[3])
+{
+ unsigned int i, index;
+
+ index = VIMC_FRAME_INDEX(lin, col, vdeb->sink_fmt.width, 3);
+ for (i = 0; i < 3; i++)
+ vdeb->src_frame[index + i] = rgb[i];
+}
+
+static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable) {
+ const struct vimc_pix_map *vpix;
+ unsigned int frame_size;
+
+ if (vdeb->src_frame)
+ return 0;
+
+ /* Calculate the frame size of the source pad */
+ vpix = vimc_pix_map_by_code(vdeb->src_code);
+ frame_size = vdeb->sink_fmt.width * vdeb->sink_fmt.height *
+ vpix->bpp;
+
+ /* Save the bytes per pixel of the sink */
+ vpix = vimc_pix_map_by_code(vdeb->sink_fmt.code);
+ vdeb->sink_bpp = vpix->bpp;
+
+ /* Get the corresponding pixel map from the table */
+ vdeb->sink_pix_map =
+ vimc_deb_pix_map_by_code(vdeb->sink_fmt.code);
+
+ /*
+ * Allocate the frame buffer. Use vmalloc to be able to
+ * allocate a large amount of memory
+ */
+ vdeb->src_frame = vmalloc(frame_size);
+ if (!vdeb->src_frame)
+ return -ENOMEM;
+
+ /* Turn the stream on in the subdevices directly connected */
+ ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 1);
+ if (ret) {
+ vfree(vdeb->src_frame);
+ vdeb->src_frame = NULL;
+ return ret;
+ }
+ } else {
+ if (!vdeb->src_frame)
+ return 0;
+
+ /* Disable streaming from the pipe */
+ ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 0);
+ if (ret)
+ return ret;
+
+ vfree(vdeb->src_frame);
+ vdeb->src_frame = NULL;
+ }
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops vimc_deb_video_ops = {
+ .s_stream = vimc_deb_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_deb_ops = {
+ .pad = &vimc_deb_pad_ops,
+ .video = &vimc_deb_video_ops,
+};
+
+static unsigned int vimc_deb_get_val(const u8 *bytes,
+ const unsigned int n_bytes)
+{
+ unsigned int i;
+ unsigned int acc = 0;
+
+ for (i = 0; i < n_bytes; i++)
+ acc = acc + (bytes[i] << (8 * i));
+
+ return acc;
+}
+
+static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb,
+ const u8 *frame,
+ const unsigned int lin,
+ const unsigned int col,
+ unsigned int rgb[3])
+{
+ unsigned int i, seek, wlin, wcol;
+ unsigned int n_rgb[3] = {0, 0, 0};
+
+ for (i = 0; i < 3; i++)
+ rgb[i] = 0;
+
+ /*
+ * Calculate how many we need to subtract to get to the pixel in
+ * the top left corner of the mean window (considering the current
+ * pixel as the center)
+ */
+ seek = deb_mean_win_size / 2;
+
+ /* Sum the values of the colors in the mean window */
+
+ dev_dbg(vdeb->dev,
+ "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n",
+ vdeb->sd.name, lin, col, vdeb->sink_fmt.height, seek);
+
+ /*
+ * Iterate through all the lines in the mean window, start
+ * with zero if the pixel is outside the frame and don't pass
+ * the height when the pixel is in the bottom border of the
+ * frame
+ */
+ for (wlin = seek > lin ? 0 : lin - seek;
+ wlin < lin + seek + 1 && wlin < vdeb->sink_fmt.height;
+ wlin++) {
+
+ /*
+ * Iterate through all the columns in the mean window, start
+ * with zero if the pixel is outside the frame and don't pass
+ * the width when the pixel is in the right border of the
+ * frame
+ */
+ for (wcol = seek > col ? 0 : col - seek;
+ wcol < col + seek + 1 && wcol < vdeb->sink_fmt.width;
+ wcol++) {
+ enum vimc_deb_rgb_colors color;
+ unsigned int index;
+
+ /* Check which color this pixel is */
+ color = vdeb->sink_pix_map->order[wlin % 2][wcol % 2];
+
+ index = VIMC_FRAME_INDEX(wlin, wcol,
+ vdeb->sink_fmt.width,
+ vdeb->sink_bpp);
+
+ dev_dbg(vdeb->dev,
+ "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n",
+ vdeb->sd.name, index, wlin, wcol, color);
+
+ /* Get its value */
+ rgb[color] = rgb[color] +
+ vimc_deb_get_val(&frame[index], vdeb->sink_bpp);
+
+ /* Save how many values we already added */
+ n_rgb[color]++;
+
+ dev_dbg(vdeb->dev, "deb: %s: RGB CALC: val %d, n %d\n",
+ vdeb->sd.name, rgb[color], n_rgb[color]);
+ }
+ }
+
+ /* Calculate the mean */
+ for (i = 0; i < 3; i++) {
+ dev_dbg(vdeb->dev,
+ "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n",
+ vdeb->sd.name, lin, col, i, rgb[i], n_rgb[i]);
+
+ if (n_rgb[i])
+ rgb[i] = rgb[i] / n_rgb[i];
+
+ dev_dbg(vdeb->dev,
+ "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n",
+ vdeb->sd.name, lin, col, i, rgb[i]);
+ }
+}
+
+static void vimc_deb_process_frame(struct vimc_ent_device *ved,
+ struct media_pad *sink,
+ const void *sink_frame)
+{
+ struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
+ ved);
+ unsigned int rgb[3];
+ unsigned int i, j;
+
+ /* If the stream in this node is not active, just return */
+ if (!vdeb->src_frame)
+ return;
+
+ for (i = 0; i < vdeb->sink_fmt.height; i++)
+ for (j = 0; j < vdeb->sink_fmt.width; j++) {
+ vimc_deb_calc_rgb_sink(vdeb, sink_frame, i, j, rgb);
+ vdeb->set_rgb_src(vdeb, i, j, rgb);
+ }
+
+ /* Propagate the frame through all source pads */
+ for (i = 1; i < vdeb->sd.entity.num_pads; i++) {
+ struct media_pad *pad = &vdeb->sd.entity.pads[i];
+
+ vimc_propagate_frame(pad, vdeb->src_frame);
+ }
+}
+
+static void vimc_deb_comp_unbind(struct device *comp, struct device *master,
+ void *master_data)
+{
+ struct vimc_ent_device *ved = dev_get_drvdata(comp);
+ struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
+ ved);
+
+ vimc_ent_sd_unregister(ved, &vdeb->sd);
+ kfree(vdeb);
+}
+
+static int vimc_deb_comp_bind(struct device *comp, struct device *master,
+ void *master_data)
+{
+ struct v4l2_device *v4l2_dev = master_data;
+ struct vimc_platform_data *pdata = comp->platform_data;
+ struct vimc_deb_device *vdeb;
+ int ret;
+
+ /* Allocate the vdeb struct */
+ vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL);
+ if (!vdeb)
+ return -ENOMEM;
+
+ /* Initialize ved and sd */
+ ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
+ pdata->entity_name,
+ MEDIA_ENT_F_ATV_DECODER, 2,
+ (const unsigned long[2]) {MEDIA_PAD_FL_SINK,
+ MEDIA_PAD_FL_SOURCE},
+ &vimc_deb_ops);
+ if (ret) {
+ kfree(vdeb);
+ return ret;
+ }
+
+ vdeb->ved.process_frame = vimc_deb_process_frame;
+ dev_set_drvdata(comp, &vdeb->ved);
+ vdeb->dev = comp;
+
+ /* Initialize the frame format */
+ vdeb->sink_fmt = sink_fmt_default;
+ /*
+ * TODO: Add support for more output formats, we only support
+ * RGB888 for now
+ * NOTE: the src format is always the same as the sink, except
+ * for the code
+ */
+ vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24;
+ vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24;
+
+ return 0;
+}
+
+static const struct component_ops vimc_deb_comp_ops = {
+ .bind = vimc_deb_comp_bind,
+ .unbind = vimc_deb_comp_unbind,
+};
+
+static int vimc_deb_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &vimc_deb_comp_ops);
+}
+
+static int vimc_deb_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vimc_deb_comp_ops);
+
+ return 0;
+}
+
+static const struct platform_device_id vimc_deb_driver_ids[] = {
+ {
+ .name = VIMC_DEB_DRV_NAME,
+ },
+ { }
+};
+
+static struct platform_driver vimc_deb_pdrv = {
+ .probe = vimc_deb_probe,
+ .remove = vimc_deb_remove,
+ .id_table = vimc_deb_driver_ids,
+ .driver = {
+ .name = VIMC_DEB_DRV_NAME,
+ },
+};
+
+module_platform_driver(vimc_deb_pdrv);
+
+MODULE_DEVICE_TABLE(platform, vimc_deb_driver_ids);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Debayer");
+MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
new file mode 100644
index 000000000000..0a3e086e12f3
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -0,0 +1,456 @@
+/*
+ * vimc-scaler.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-common.h"
+
+#define VIMC_SCA_DRV_NAME "vimc-scaler"
+
+static unsigned int sca_mult = 3;
+module_param(sca_mult, uint, 0000);
+MODULE_PARM_DESC(sca_mult, " the image size multiplier");
+
+#define IS_SINK(pad) (!pad)
+#define IS_SRC(pad) (pad)
+#define MAX_ZOOM 8
+
+struct vimc_sca_device {
+ struct vimc_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+ /* NOTE: the source fmt is the same as the sink
+ * with the width and hight multiplied by mult
+ */
+ struct v4l2_mbus_framefmt sink_fmt;
+ /* Values calculated when the stream starts */
+ u8 *src_frame;
+ unsigned int src_line_size;
+ unsigned int bpp;
+};
+
+static const struct v4l2_mbus_framefmt sink_fmt_default = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+};
+
+static int vimc_sca_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int i;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+ *mf = sink_fmt_default;
+
+ for (i = 1; i < sd->entity.num_pads; i++) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, i);
+ *mf = sink_fmt_default;
+ mf->width = mf->width * sca_mult;
+ mf->height = mf->height * sca_mult;
+ }
+
+ return 0;
+}
+
+static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index);
+
+ /* We don't support bayer format */
+ if (!vpix || vpix->bayer)
+ return -EINVAL;
+
+ code->code = vpix->code;
+
+ return 0;
+}
+
+static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct vimc_pix_map *vpix;
+
+ if (fse->index)
+ return -EINVAL;
+
+ /* Only accept code in the pix map table in non bayer format */
+ vpix = vimc_pix_map_by_code(fse->code);
+ if (!vpix || vpix->bayer)
+ return -EINVAL;
+
+ fse->min_width = VIMC_FRAME_MIN_WIDTH;
+ fse->min_height = VIMC_FRAME_MIN_HEIGHT;
+
+ if (IS_SINK(fse->pad)) {
+ fse->max_width = VIMC_FRAME_MAX_WIDTH;
+ fse->max_height = VIMC_FRAME_MAX_HEIGHT;
+ } else {
+ fse->max_width = VIMC_FRAME_MAX_WIDTH * MAX_ZOOM;
+ fse->max_height = VIMC_FRAME_MAX_HEIGHT * MAX_ZOOM;
+ }
+
+ return 0;
+}
+
+static int vimc_sca_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
+
+ /* Get the current sink format */
+ format->format = (format->which == V4L2_SUBDEV_FORMAT_TRY) ?
+ *v4l2_subdev_get_try_format(sd, cfg, 0) :
+ vsca->sink_fmt;
+
+ /* Scale the frame size for the source pad */
+ if (IS_SRC(format->pad)) {
+ format->format.width = vsca->sink_fmt.width * sca_mult;
+ format->format.height = vsca->sink_fmt.height * sca_mult;
+ }
+
+ return 0;
+}
+
+static void vimc_sca_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ const struct vimc_pix_map *vpix;
+
+ /* Only accept code in the pix map table in non bayer format */
+ vpix = vimc_pix_map_by_code(fmt->code);
+ if (!vpix || vpix->bayer)
+ fmt->code = sink_fmt_default.code;
+
+ fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
+ VIMC_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
+ VIMC_FRAME_MAX_HEIGHT) & ~1;
+
+ if (fmt->field == V4L2_FIELD_ANY)
+ fmt->field = sink_fmt_default.field;
+
+ vimc_colorimetry_clamp(fmt);
+}
+
+static int vimc_sca_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ /* Do not change the format while stream is on */
+ if (vsca->src_frame)
+ return -EBUSY;
+
+ sink_fmt = &vsca->sink_fmt;
+ } else {
+ sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+ }
+
+ /*
+ * Do not change the format of the source pad,
+ * it is propagated from the sink
+ */
+ if (IS_SRC(fmt->pad)) {
+ fmt->format = *sink_fmt;
+ fmt->format.width = sink_fmt->width * sca_mult;
+ fmt->format.height = sink_fmt->height * sca_mult;
+ } else {
+ /* Set the new format in the sink pad */
+ vimc_sca_adjust_sink_fmt(&fmt->format);
+
+ dev_dbg(vsca->dev, "%s: sink format update: "
+ "old:%dx%d (0x%x, %d, %d, %d, %d) "
+ "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name,
+ /* old */
+ sink_fmt->width, sink_fmt->height, sink_fmt->code,
+ sink_fmt->colorspace, sink_fmt->quantization,
+ sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
+ /* new */
+ fmt->format.width, fmt->format.height, fmt->format.code,
+ fmt->format.colorspace, fmt->format.quantization,
+ fmt->format.xfer_func, fmt->format.ycbcr_enc);
+
+ *sink_fmt = fmt->format;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = {
+ .init_cfg = vimc_sca_init_cfg,
+ .enum_mbus_code = vimc_sca_enum_mbus_code,
+ .enum_frame_size = vimc_sca_enum_frame_size,
+ .get_fmt = vimc_sca_get_fmt,
+ .set_fmt = vimc_sca_set_fmt,
+};
+
+static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable) {
+ const struct vimc_pix_map *vpix;
+ unsigned int frame_size;
+
+ if (vsca->src_frame)
+ return 0;
+
+ /* Save the bytes per pixel of the sink */
+ vpix = vimc_pix_map_by_code(vsca->sink_fmt.code);
+ vsca->bpp = vpix->bpp;
+
+ /* Calculate the width in bytes of the src frame */
+ vsca->src_line_size = vsca->sink_fmt.width *
+ sca_mult * vsca->bpp;
+
+ /* Calculate the frame size of the source pad */
+ frame_size = vsca->src_line_size * vsca->sink_fmt.height *
+ sca_mult;
+
+ /* Allocate the frame buffer. Use vmalloc to be able to
+ * allocate a large amount of memory
+ */
+ vsca->src_frame = vmalloc(frame_size);
+ if (!vsca->src_frame)
+ return -ENOMEM;
+
+ /* Turn the stream on in the subdevices directly connected */
+ ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1);
+ if (ret) {
+ vfree(vsca->src_frame);
+ vsca->src_frame = NULL;
+ return ret;
+ }
+ } else {
+ if (!vsca->src_frame)
+ return 0;
+
+ /* Disable streaming from the pipe */
+ ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0);
+ if (ret)
+ return ret;
+
+ vfree(vsca->src_frame);
+ vsca->src_frame = NULL;
+ }
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops vimc_sca_video_ops = {
+ .s_stream = vimc_sca_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sca_ops = {
+ .pad = &vimc_sca_pad_ops,
+ .video = &vimc_sca_video_ops,
+};
+
+static void vimc_sca_fill_pix(u8 *const ptr,
+ const u8 *const pixel,
+ const unsigned int bpp)
+{
+ unsigned int i;
+
+ /* copy the pixel to the pointer */
+ for (i = 0; i < bpp; i++)
+ ptr[i] = pixel[i];
+}
+
+static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca,
+ const unsigned int lin, const unsigned int col,
+ const u8 *const sink_frame)
+{
+ unsigned int i, j, index;
+ const u8 *pixel;
+
+ /* Point to the pixel value in position (lin, col) in the sink frame */
+ index = VIMC_FRAME_INDEX(lin, col,
+ vsca->sink_fmt.width,
+ vsca->bpp);
+ pixel = &sink_frame[index];
+
+ dev_dbg(vsca->dev,
+ "sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n",
+ vsca->sd.name, lin, col, index);
+
+ /* point to the place we are going to put the first pixel
+ * in the scaled src frame
+ */
+ index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult,
+ vsca->sink_fmt.width * sca_mult, vsca->bpp);
+
+ dev_dbg(vsca->dev, "sca: %s: scale_pix src pos %dx%d, index %d\n",
+ vsca->sd.name, lin * sca_mult, col * sca_mult, index);
+
+ /* Repeat this pixel mult times */
+ for (i = 0; i < sca_mult; i++) {
+ /* Iterate through each beginning of a
+ * pixel repetition in a line
+ */
+ for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) {
+ dev_dbg(vsca->dev,
+ "sca: %s: sca: scale_pix src pos %d\n",
+ vsca->sd.name, index + j);
+
+ /* copy the pixel to the position index + j */
+ vimc_sca_fill_pix(&vsca->src_frame[index + j],
+ pixel, vsca->bpp);
+ }
+
+ /* move the index to the next line */
+ index += vsca->src_line_size;
+ }
+}
+
+static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca,
+ const u8 *const sink_frame)
+{
+ unsigned int i, j;
+
+ /* Scale each pixel from the original sink frame */
+ /* TODO: implement scale down, only scale up is supported for now */
+ for (i = 0; i < vsca->sink_fmt.height; i++)
+ for (j = 0; j < vsca->sink_fmt.width; j++)
+ vimc_sca_scale_pix(vsca, i, j, sink_frame);
+}
+
+static void vimc_sca_process_frame(struct vimc_ent_device *ved,
+ struct media_pad *sink,
+ const void *sink_frame)
+{
+ struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
+ ved);
+ unsigned int i;
+
+ /* If the stream in this node is not active, just return */
+ if (!vsca->src_frame)
+ return;
+
+ vimc_sca_fill_src_frame(vsca, sink_frame);
+
+ /* Propagate the frame through all source pads */
+ for (i = 1; i < vsca->sd.entity.num_pads; i++) {
+ struct media_pad *pad = &vsca->sd.entity.pads[i];
+
+ vimc_propagate_frame(pad, vsca->src_frame);
+ }
+};
+
+static void vimc_sca_comp_unbind(struct device *comp, struct device *master,
+ void *master_data)
+{
+ struct vimc_ent_device *ved = dev_get_drvdata(comp);
+ struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
+ ved);
+
+ vimc_ent_sd_unregister(ved, &vsca->sd);
+ kfree(vsca);
+}
+
+
+static int vimc_sca_comp_bind(struct device *comp, struct device *master,
+ void *master_data)
+{
+ struct v4l2_device *v4l2_dev = master_data;
+ struct vimc_platform_data *pdata = comp->platform_data;
+ struct vimc_sca_device *vsca;
+ int ret;
+
+ /* Allocate the vsca struct */
+ vsca = kzalloc(sizeof(*vsca), GFP_KERNEL);
+ if (!vsca)
+ return -ENOMEM;
+
+ /* Initialize ved and sd */
+ ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
+ pdata->entity_name,
+ MEDIA_ENT_F_ATV_DECODER, 2,
+ (const unsigned long[2]) {MEDIA_PAD_FL_SINK,
+ MEDIA_PAD_FL_SOURCE},
+ &vimc_sca_ops);
+ if (ret) {
+ kfree(vsca);
+ return ret;
+ }
+
+ vsca->ved.process_frame = vimc_sca_process_frame;
+ dev_set_drvdata(comp, &vsca->ved);
+ vsca->dev = comp;
+
+ /* Initialize the frame format */
+ vsca->sink_fmt = sink_fmt_default;
+
+ return 0;
+}
+
+static const struct component_ops vimc_sca_comp_ops = {
+ .bind = vimc_sca_comp_bind,
+ .unbind = vimc_sca_comp_unbind,
+};
+
+static int vimc_sca_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &vimc_sca_comp_ops);
+}
+
+static int vimc_sca_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vimc_sca_comp_ops);
+
+ return 0;
+}
+
+static const struct platform_device_id vimc_sca_driver_ids[] = {
+ {
+ .name = VIMC_SCA_DRV_NAME,
+ },
+ { }
+};
+
+static struct platform_driver vimc_sca_pdrv = {
+ .probe = vimc_sca_probe,
+ .remove = vimc_sca_remove,
+ .id_table = vimc_sca_driver_ids,
+ .driver = {
+ .name = VIMC_SCA_DRV_NAME,
+ },
+};
+
+module_platform_driver(vimc_sca_pdrv);
+
+MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Scaler");
+MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 591f6a4f8bd3..615c2b18dcfc 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -15,36 +15,64 @@
*
*/
+#include <linux/component.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/v4l2-mediabus.h>
#include <linux/vmalloc.h>
#include <media/v4l2-subdev.h>
+#include <media/v4l2-tpg.h>
-#include "vimc-sensor.h"
+#include "vimc-common.h"
+
+#define VIMC_SEN_DRV_NAME "vimc-sensor"
struct vimc_sen_device {
struct vimc_ent_device ved;
struct v4l2_subdev sd;
+ struct device *dev;
+ struct tpg_data tpg;
struct task_struct *kthread_sen;
u8 *frame;
/* The active format */
struct v4l2_mbus_framefmt mbus_format;
- int frame_size;
};
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+};
+
+static int vimc_sen_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ unsigned int i;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, i);
+ *mf = fmt_default;
+ }
+
+ return 0;
+}
+
static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
- struct vimc_sen_device *vsen =
- container_of(sd, struct vimc_sen_device, sd);
+ const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index);
- /* TODO: Add support for other codes */
- if (code->index)
+ if (!vpix)
return -EINVAL;
- code->code = vsen->mbus_format.code;
+ code->code = vpix->code;
return 0;
}
@@ -53,51 +81,123 @@ static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
- struct vimc_sen_device *vsen =
- container_of(sd, struct vimc_sen_device, sd);
+ const struct vimc_pix_map *vpix;
- /* TODO: Add support to other formats */
if (fse->index)
return -EINVAL;
- /* TODO: Add support for other codes */
- if (fse->code != vsen->mbus_format.code)
+ /* Only accept code in the pix map table */
+ vpix = vimc_pix_map_by_code(fse->code);
+ if (!vpix)
return -EINVAL;
- fse->min_width = vsen->mbus_format.width;
- fse->max_width = vsen->mbus_format.width;
- fse->min_height = vsen->mbus_format.height;
- fse->max_height = vsen->mbus_format.height;
+ fse->min_width = VIMC_FRAME_MIN_WIDTH;
+ fse->max_width = VIMC_FRAME_MAX_WIDTH;
+ fse->min_height = VIMC_FRAME_MIN_HEIGHT;
+ fse->max_height = VIMC_FRAME_MAX_HEIGHT;
return 0;
}
static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
+ struct v4l2_subdev_format *fmt)
{
struct vimc_sen_device *vsen =
container_of(sd, struct vimc_sen_device, sd);
- format->format = vsen->mbus_format;
+ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) :
+ vsen->mbus_format;
+
+ return 0;
+}
+
+static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
+{
+ const struct vimc_pix_map *vpix =
+ vimc_pix_map_by_code(vsen->mbus_format.code);
+
+ tpg_reset_source(&vsen->tpg, vsen->mbus_format.width,
+ vsen->mbus_format.height, vsen->mbus_format.field);
+ tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp);
+ tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
+ tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
+ /* TODO: add support for V4L2_FIELD_ALTERNATE */
+ tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false);
+ tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
+ tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
+ tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
+ tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
+}
+
+static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ const struct vimc_pix_map *vpix;
+
+ /* Only accept code in the pix map table */
+ vpix = vimc_pix_map_by_code(fmt->code);
+ if (!vpix)
+ fmt->code = fmt_default.code;
+
+ fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
+ VIMC_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
+ VIMC_FRAME_MAX_HEIGHT) & ~1;
+
+ /* TODO: add support for V4L2_FIELD_ALTERNATE */
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = fmt_default.field;
+
+ vimc_colorimetry_clamp(fmt);
+}
+
+static int vimc_sen_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ /* Do not change the format while stream is on */
+ if (vsen->frame)
+ return -EBUSY;
+
+ mf = &vsen->mbus_format;
+ } else {
+ mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ }
+
+ /* Set the new format */
+ vimc_sen_adjust_fmt(&fmt->format);
+
+ dev_dbg(vsen->dev, "%s: format update: "
+ "old:%dx%d (0x%x, %d, %d, %d, %d) "
+ "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name,
+ /* old */
+ mf->width, mf->height, mf->code,
+ mf->colorspace, mf->quantization,
+ mf->xfer_func, mf->ycbcr_enc,
+ /* new */
+ fmt->format.width, fmt->format.height, fmt->format.code,
+ fmt->format.colorspace, fmt->format.quantization,
+ fmt->format.xfer_func, fmt->format.ycbcr_enc);
+
+ *mf = fmt->format;
return 0;
}
static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+ .init_cfg = vimc_sen_init_cfg,
.enum_mbus_code = vimc_sen_enum_mbus_code,
.enum_frame_size = vimc_sen_enum_frame_size,
.get_fmt = vimc_sen_get_fmt,
- /* TODO: Add support to other formats */
- .set_fmt = vimc_sen_get_fmt,
+ .set_fmt = vimc_sen_set_fmt,
};
-/* media operations */
-static const struct media_entity_operations vimc_sen_mops = {
- .link_validate = v4l2_subdev_link_validate,
-};
-
-static int vimc_thread_sen(void *data)
+static int vimc_sen_tpg_thread(void *data)
{
struct vimc_sen_device *vsen = data;
unsigned int i;
@@ -110,7 +210,7 @@ static int vimc_thread_sen(void *data)
if (kthread_should_stop())
break;
- memset(vsen->frame, 100, vsen->frame_size);
+ tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
/* Send the frame to all source pads */
for (i = 0; i < vsen->sd.entity.num_pads; i++)
@@ -132,50 +232,57 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
if (enable) {
const struct vimc_pix_map *vpix;
+ unsigned int frame_size;
if (vsen->kthread_sen)
- return -EINVAL;
+ /* tpg is already executing */
+ return 0;
/* Calculate the frame size */
vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
- vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
- vsen->mbus_format.height;
+ frame_size = vsen->mbus_format.width * vpix->bpp *
+ vsen->mbus_format.height;
/*
* Allocate the frame buffer. Use vmalloc to be able to
* allocate a large amount of memory
*/
- vsen->frame = vmalloc(vsen->frame_size);
+ vsen->frame = vmalloc(frame_size);
if (!vsen->frame)
return -ENOMEM;
+ /* configure the test pattern generator */
+ vimc_sen_tpg_s_format(vsen);
+
/* Initialize the image generator thread */
- vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
- vsen->sd.v4l2_dev->name);
+ vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen,
+ "%s-sen", vsen->sd.v4l2_dev->name);
if (IS_ERR(vsen->kthread_sen)) {
- dev_err(vsen->sd.v4l2_dev->dev,
- "%s: kernel_thread() failed\n", vsen->sd.name);
+ dev_err(vsen->dev, "%s: kernel_thread() failed\n",
+ vsen->sd.name);
vfree(vsen->frame);
vsen->frame = NULL;
return PTR_ERR(vsen->kthread_sen);
}
} else {
if (!vsen->kthread_sen)
- return -EINVAL;
+ return 0;
/* Stop image generator */
ret = kthread_stop(vsen->kthread_sen);
- vsen->kthread_sen = NULL;
+ if (ret)
+ return ret;
+ vsen->kthread_sen = NULL;
vfree(vsen->frame);
vsen->frame = NULL;
- return ret;
+ return 0;
}
return 0;
}
-struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+static struct v4l2_subdev_video_ops vimc_sen_video_ops = {
.s_stream = vimc_sen_s_stream,
};
@@ -184,93 +291,100 @@ static const struct v4l2_subdev_ops vimc_sen_ops = {
.video = &vimc_sen_video_ops,
};
-static void vimc_sen_destroy(struct vimc_ent_device *ved)
+static void vimc_sen_comp_unbind(struct device *comp, struct device *master,
+ void *master_data)
{
+ struct vimc_ent_device *ved = dev_get_drvdata(comp);
struct vimc_sen_device *vsen =
container_of(ved, struct vimc_sen_device, ved);
- v4l2_device_unregister_subdev(&vsen->sd);
- media_entity_cleanup(ved->ent);
+ vimc_ent_sd_unregister(ved, &vsen->sd);
+ tpg_free(&vsen->tpg);
kfree(vsen);
}
-struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
- const char *const name,
- u16 num_pads,
- const unsigned long *pads_flag)
+static int vimc_sen_comp_bind(struct device *comp, struct device *master,
+ void *master_data)
{
+ struct v4l2_device *v4l2_dev = master_data;
+ struct vimc_platform_data *pdata = comp->platform_data;
struct vimc_sen_device *vsen;
- unsigned int i;
int ret;
- /* NOTE: a sensor node may be created with more then one pad */
- if (!name || !num_pads || !pads_flag)
- return ERR_PTR(-EINVAL);
-
- /* check if all pads are sources */
- for (i = 0; i < num_pads; i++)
- if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
- return ERR_PTR(-EINVAL);
-
/* Allocate the vsen struct */
vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
if (!vsen)
- return ERR_PTR(-ENOMEM);
-
- /* Allocate the pads */
- vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
- if (IS_ERR(vsen->ved.pads)) {
- ret = PTR_ERR(vsen->ved.pads);
+ return -ENOMEM;
+
+ /* Initialize ved and sd */
+ ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
+ pdata->entity_name,
+ MEDIA_ENT_F_ATV_DECODER, 1,
+ (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE},
+ &vimc_sen_ops);
+ if (ret)
goto err_free_vsen;
- }
-
- /* Fill the vimc_ent_device struct */
- vsen->ved.destroy = vimc_sen_destroy;
- vsen->ved.ent = &vsen->sd.entity;
- /* Initialize the subdev */
- v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
- vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
- vsen->sd.entity.ops = &vimc_sen_mops;
- vsen->sd.owner = THIS_MODULE;
- strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
- v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
+ dev_set_drvdata(comp, &vsen->ved);
+ vsen->dev = comp;
- /* Expose this subdev to user space */
- vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ /* Initialize the frame format */
+ vsen->mbus_format = fmt_default;
- /* Initialize the media entity */
- ret = media_entity_pads_init(&vsen->sd.entity,
- num_pads, vsen->ved.pads);
+ /* Initialize the test pattern generator */
+ tpg_init(&vsen->tpg, vsen->mbus_format.width,
+ vsen->mbus_format.height);
+ ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH);
if (ret)
- goto err_clean_pads;
-
- /* Set the active frame format (this is hardcoded for now) */
- vsen->mbus_format.width = 640;
- vsen->mbus_format.height = 480;
- vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
- vsen->mbus_format.field = V4L2_FIELD_NONE;
- vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
- vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
- vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
-
- /* Register the subdev with the v4l2 and the media framework */
- ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
- if (ret) {
- dev_err(vsen->sd.v4l2_dev->dev,
- "%s: subdev register failed (err=%d)\n",
- vsen->sd.name, ret);
- goto err_clean_m_ent;
- }
+ goto err_unregister_ent_sd;
- return &vsen->ved;
+ return 0;
-err_clean_m_ent:
- media_entity_cleanup(&vsen->sd.entity);
-err_clean_pads:
- vimc_pads_cleanup(vsen->ved.pads);
+err_unregister_ent_sd:
+ vimc_ent_sd_unregister(&vsen->ved, &vsen->sd);
err_free_vsen:
kfree(vsen);
- return ERR_PTR(ret);
+ return ret;
}
+
+static const struct component_ops vimc_sen_comp_ops = {
+ .bind = vimc_sen_comp_bind,
+ .unbind = vimc_sen_comp_unbind,
+};
+
+static int vimc_sen_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &vimc_sen_comp_ops);
+}
+
+static int vimc_sen_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vimc_sen_comp_ops);
+
+ return 0;
+}
+
+static const struct platform_device_id vimc_sen_driver_ids[] = {
+ {
+ .name = VIMC_SEN_DRV_NAME,
+ },
+ { }
+};
+
+static struct platform_driver vimc_sen_pdrv = {
+ .probe = vimc_sen_probe,
+ .remove = vimc_sen_remove,
+ .id_table = vimc_sen_driver_ids,
+ .driver = {
+ .name = VIMC_SEN_DRV_NAME,
+ },
+};
+
+module_platform_driver(vimc_sen_pdrv);
+
+MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor");
+MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
deleted file mode 100644
index 505310e8aeb7..000000000000
--- a/drivers/media/platform/vimc/vimc-sensor.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * vimc-sensor.h Virtual Media Controller Driver
- *
- * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _VIMC_SENSOR_H_
-#define _VIMC_SENSOR_H_
-
-#include "vimc-core.h"
-
-struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
- const char *const name,
- u16 num_pads,
- const unsigned long *pads_flag);
-
-#endif
diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c
index 653f4099f737..e15705758969 100644
--- a/drivers/media/platform/vivid/vivid-cec.c
+++ b/drivers/media/platform/vivid/vivid-cec.c
@@ -34,7 +34,7 @@ void vivid_cec_bus_free_work(struct vivid_dev *dev)
cancel_delayed_work_sync(&cw->work);
spin_lock(&dev->cec_slock);
list_del(&cw->list);
- cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0);
+ cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE);
kfree(cw);
}
spin_unlock(&dev->cec_slock);
@@ -84,7 +84,7 @@ static void vivid_cec_xfer_done_worker(struct work_struct *work)
dev->cec_xfer_start_jiffies = 0;
list_del(&cw->list);
spin_unlock(&dev->cec_slock);
- cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0);
+ cec_transmit_attempt_done(cw->adap, cw->tx_status);
/* Broadcast message */
if (adap != dev->cec_rx_adap)
@@ -105,7 +105,7 @@ static void vivid_cec_xfer_try_worker(struct work_struct *work)
if (dev->cec_xfer_time_jiffies) {
list_del(&cw->list);
spin_unlock(&dev->cec_slock);
- cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
+ cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_ARB_LOST);
kfree(cw);
} else {
INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 85387a64179a..847963b6e9eb 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -74,6 +74,7 @@ struct vsp1_device {
void __iomem *mmio;
struct rcar_fcp_device *fcp;
+ struct device *bus_master;
struct vsp1_bru *bru;
struct vsp1_clu *clu;
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 7d8f37772b56..aaf17b13fd78 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -137,7 +137,7 @@ static int vsp1_dl_body_init(struct vsp1_device *vsp1,
dlb->vsp1 = vsp1;
dlb->size = size;
- dlb->entries = dma_alloc_wc(vsp1->dev, dlb->size, &dlb->dma,
+ dlb->entries = dma_alloc_wc(vsp1->bus_master, dlb->size, &dlb->dma,
GFP_KERNEL);
if (!dlb->entries)
return -ENOMEM;
@@ -150,7 +150,7 @@ static int vsp1_dl_body_init(struct vsp1_device *vsp1,
*/
static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
{
- dma_free_wc(dlb->vsp1->dev, dlb->size, dlb->entries, dlb->dma);
+ dma_free_wc(dlb->vsp1->bus_master, dlb->size, dlb->entries, dlb->dma);
}
/**
@@ -561,9 +561,19 @@ void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
spin_unlock(&dlm->lock);
}
-void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
+/**
+ * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
+ * @dlm: the display list manager
+ *
+ * Return true if the previous display list has completed at frame end, or false
+ * if it has been delayed by one frame because the display list commit raced
+ * with the frame end interrupt. The function always returns true in header mode
+ * as display list processing is then not continuous and races never occur.
+ */
+bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
{
struct vsp1_device *vsp1 = dlm->vsp1;
+ bool completed = false;
spin_lock(&dlm->lock);
@@ -575,8 +585,10 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
* perform any operation as there can't be any new display list queued
* in that case.
*/
- if (dlm->mode == VSP1_DL_MODE_HEADER)
+ if (dlm->mode == VSP1_DL_MODE_HEADER) {
+ completed = true;
goto done;
+ }
/*
* The UPD bit set indicates that the commit operation raced with the
@@ -594,6 +606,7 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
if (dlm->queued) {
dlm->active = dlm->queued;
dlm->queued = NULL;
+ completed = true;
}
/*
@@ -614,6 +627,8 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
done:
spin_unlock(&dlm->lock);
+
+ return completed;
}
/* Hardware Setup */
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index 7131aa3c5978..6ec1380a10af 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -28,7 +28,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
-void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 9d235e830f5a..9377aafa8996 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -12,6 +12,7 @@
*/
#include <linux/device.h>
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <media/media-entity.h>
@@ -36,6 +37,14 @@ void vsp1_drm_display_start(struct vsp1_device *vsp1)
vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm);
}
+static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm *drm = to_vsp1_drm(pipe);
+
+ if (drm->du_complete)
+ drm->du_complete(drm->du_private);
+}
+
/* -----------------------------------------------------------------------------
* DU Driver API
*/
@@ -95,6 +104,7 @@ int vsp1_du_setup_lif(struct device *dev, const struct vsp1_du_lif_config *cfg)
}
pipe->num_inputs = 0;
+ vsp1->drm->du_complete = NULL;
vsp1_dlm_reset(pipe->output->dlm);
vsp1_device_put(vsp1);
@@ -199,6 +209,13 @@ int vsp1_du_setup_lif(struct device *dev, const struct vsp1_du_lif_config *cfg)
if (ret < 0)
return ret;
+ /*
+ * Register a callback to allow us to notify the DRM driver of frame
+ * completion events.
+ */
+ vsp1->drm->du_complete = cfg->callback;
+ vsp1->drm->du_private = cfg->callback_data;
+
ret = media_pipeline_start(&pipe->output->entity.subdev.entity,
&pipe->pipe);
if (ret < 0) {
@@ -524,6 +541,29 @@ void vsp1_du_atomic_flush(struct device *dev)
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
+int vsp1_du_map_sg(struct device *dev, struct sg_table *sgt)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ /*
+ * As all the buffers allocated by the DU driver are coherent, we can
+ * skip cache sync. This will need to be revisited when support for
+ * non-coherent buffers will be added to the DU driver.
+ */
+ return dma_map_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
+ DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+}
+EXPORT_SYMBOL_GPL(vsp1_du_map_sg);
+
+void vsp1_du_unmap_sg(struct device *dev, struct sg_table *sgt)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ dma_unmap_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
+ DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+}
+EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg);
+
/* -----------------------------------------------------------------------------
* Initialization
*/
@@ -603,6 +643,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
pipe->output->pipe = pipe;
+ pipe->frame_end = vsp1_du_pipeline_frame_end;
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index c8d2f88fc483..e9f80727ff92 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -23,6 +23,8 @@
* @num_inputs: number of active pipeline inputs at the beginning of an update
* @inputs: source crop rectangle, destination compose rectangle and z-order
* position for every input
+ * @du_complete: frame completion callback for the DU driver (optional)
+ * @du_private: data to be passed to the du_complete callback
*/
struct vsp1_drm {
struct vsp1_pipeline pipe;
@@ -33,8 +35,17 @@ struct vsp1_drm {
struct v4l2_rect compose;
unsigned int zpos;
} inputs[VSP1_MAX_RPF];
+
+ /* Frame synchronisation */
+ void (*du_complete)(void *);
+ void *du_private;
};
+static inline struct vsp1_drm *to_vsp1_drm(struct vsp1_pipeline *pipe)
+{
+ return container_of(pipe, struct vsp1_drm, pipe);
+}
+
int vsp1_drm_init(struct vsp1_device *vsp1);
void vsp1_drm_cleanup(struct vsp1_device *vsp1);
int vsp1_drm_create_links(struct vsp1_device *vsp1);
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 048446af5ae7..95c26edead85 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -764,6 +764,15 @@ static int vsp1_probe(struct platform_device *pdev)
PTR_ERR(vsp1->fcp));
return PTR_ERR(vsp1->fcp);
}
+
+ /*
+ * When the FCP is present, it handles all bus master accesses
+ * for the VSP and must thus be used in place of the VSP device
+ * to map DMA buffers.
+ */
+ vsp1->bus_master = rcar_fcp_get_device(vsp1->fcp);
+ } else {
+ vsp1->bus_master = vsp1->dev;
}
/* Configure device parameters based on the version register. */
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index edebf3fa926f..e817623b84e0 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -330,10 +330,21 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
+ bool completed;
+
if (pipe == NULL)
return;
- vsp1_dlm_irq_frame_end(pipe->output->dlm);
+ completed = vsp1_dlm_irq_frame_end(pipe->output->dlm);
+ if (!completed) {
+ /*
+ * If the DL commit raced with the frame end interrupt, the
+ * commit ends up being postponed by one frame. Return
+ * immediately without calling the pipeline's frame end handler
+ * or incrementing the sequence number.
+ */
+ return;
+ }
if (pipe->hgo)
vsp1_hgo_frame_end(pipe->hgo);
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index eab3c3ea85d7..5af3486afe07 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1197,7 +1197,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video->queue.ops = &vsp1_video_queue_qops;
video->queue.mem_ops = &vb2_dma_contig_memops;
video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
- video->queue.dev = video->vsp1->dev;
+ video->queue.dev = video->vsp1->bus_master;
ret = vb2_queue_init(&video->queue);
if (ret < 0) {
dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index 84bae795b70d..a5d21b7c6e0b 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -2,6 +2,7 @@ config VIDEO_XILINX
tristate "Xilinx Video IP (EXPERIMENTAL)"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA
select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
---help---
Driver for Xilinx Video IP Pipelines
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index feb3b2f1d874..ac4704388920 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -22,7 +22,7 @@
#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
#include "xilinx-dma.h"
#include "xilinx-vipp.h"
@@ -74,7 +74,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
struct media_pad *local_pad;
struct media_pad *remote_pad;
struct xvip_graph_entity *ent;
- struct v4l2_of_link link;
+ struct v4l2_fwnode_link link;
struct device_node *ep = NULL;
struct device_node *next;
int ret = 0;
@@ -92,7 +92,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name);
- ret = v4l2_of_parse_link(ep, &link);
+ ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link);
if (ret < 0) {
dev_err(xdev->dev, "failed to parse link for %s\n",
ep->full_name);
@@ -103,9 +103,10 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
* the link.
*/
if (link.local_port >= local->num_pads) {
- dev_err(xdev->dev, "invalid port number %u on %s\n",
- link.local_port, link.local_node->full_name);
- v4l2_of_put_link(&link);
+ dev_err(xdev->dev, "invalid port number %u for %s\n",
+ link.local_port,
+ to_of_node(link.local_node)->full_name);
+ v4l2_fwnode_put_link(&link);
ret = -EINVAL;
break;
}
@@ -114,25 +115,28 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
if (local_pad->flags & MEDIA_PAD_FL_SINK) {
dev_dbg(xdev->dev, "skipping sink port %s:%u\n",
- link.local_node->full_name, link.local_port);
- v4l2_of_put_link(&link);
+ to_of_node(link.local_node)->full_name,
+ link.local_port);
+ v4l2_fwnode_put_link(&link);
continue;
}
/* Skip DMA engines, they will be processed separately. */
- if (link.remote_node == xdev->dev->of_node) {
+ if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) {
dev_dbg(xdev->dev, "skipping DMA port %s:%u\n",
- link.local_node->full_name, link.local_port);
- v4l2_of_put_link(&link);
+ to_of_node(link.local_node)->full_name,
+ link.local_port);
+ v4l2_fwnode_put_link(&link);
continue;
}
/* Find the remote entity. */
- ent = xvip_graph_find_entity(xdev, link.remote_node);
+ ent = xvip_graph_find_entity(xdev,
+ to_of_node(link.remote_node));
if (ent == NULL) {
dev_err(xdev->dev, "no entity found for %s\n",
- link.remote_node->full_name);
- v4l2_of_put_link(&link);
+ to_of_node(link.remote_node)->full_name);
+ v4l2_fwnode_put_link(&link);
ret = -ENODEV;
break;
}
@@ -141,15 +145,16 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
if (link.remote_port >= remote->num_pads) {
dev_err(xdev->dev, "invalid port number %u on %s\n",
- link.remote_port, link.remote_node->full_name);
- v4l2_of_put_link(&link);
+ link.remote_port,
+ to_of_node(link.remote_node)->full_name);
+ v4l2_fwnode_put_link(&link);
ret = -EINVAL;
break;
}
remote_pad = &remote->pads[link.remote_port];
- v4l2_of_put_link(&link);
+ v4l2_fwnode_put_link(&link);
/* Create the media link. */
dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n",
@@ -194,7 +199,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
struct media_pad *source_pad;
struct media_pad *sink_pad;
struct xvip_graph_entity *ent;
- struct v4l2_of_link link;
+ struct v4l2_fwnode_link link;
struct device_node *ep = NULL;
struct device_node *next;
struct xvip_dma *dma;
@@ -213,7 +218,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
dev_dbg(xdev->dev, "processing endpoint %s\n", ep->full_name);
- ret = v4l2_of_parse_link(ep, &link);
+ ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link);
if (ret < 0) {
dev_err(xdev->dev, "failed to parse link for %s\n",
ep->full_name);
@@ -225,7 +230,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
if (dma == NULL) {
dev_err(xdev->dev, "no DMA engine found for port %u\n",
link.local_port);
- v4l2_of_put_link(&link);
+ v4l2_fwnode_put_link(&link);
ret = -EINVAL;
break;
}
@@ -234,19 +239,21 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
dma->video.name);
/* Find the remote entity. */
- ent = xvip_graph_find_entity(xdev, link.remote_node);
+ ent = xvip_graph_find_entity(xdev,
+ to_of_node(link.remote_node));
if (ent == NULL) {
dev_err(xdev->dev, "no entity found for %s\n",
- link.remote_node->full_name);
- v4l2_of_put_link(&link);
+ to_of_node(link.remote_node)->full_name);
+ v4l2_fwnode_put_link(&link);
ret = -ENODEV;
break;
}
if (link.remote_port >= ent->entity->num_pads) {
dev_err(xdev->dev, "invalid port number %u on %s\n",
- link.remote_port, link.remote_node->full_name);
- v4l2_of_put_link(&link);
+ link.remote_port,
+ to_of_node(link.remote_node)->full_name);
+ v4l2_fwnode_put_link(&link);
ret = -EINVAL;
break;
}
@@ -263,7 +270,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
sink_pad = &dma->pad;
}
- v4l2_of_put_link(&link);
+ v4l2_fwnode_put_link(&link);
/* Create the media link. */
dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n",
@@ -383,8 +390,8 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev,
}
entity->node = remote;
- entity->asd.match_type = V4L2_ASYNC_MATCH_OF;
- entity->asd.match.of.node = remote;
+ entity->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ entity->asd.match.fwnode.fwnode = of_fwnode_handle(remote);
list_add_tail(&entity->list, &xdev->entities);
xdev->num_subdevs++;
}
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 7240223dc15a..17e82a9a0109 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -610,10 +610,21 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode)
}
}
- if (radio->rds_on)
+ if (radio->rds_on) {
r = core->write(core, WL1273_RDS_DATA_ENB, 1);
- else
+ if (r) {
+ dev_err(dev, "%s: RDS_DATA_ENB ON fails\n",
+ __func__);
+ goto fail;
+ }
+ } else {
r = core->write(core, WL1273_RDS_DATA_ENB, 0);
+ if (r) {
+ dev_err(dev, "%s: RDS_DATA_ENB OFF fails\n",
+ __func__);
+ goto fail;
+ }
+ }
} else {
dev_warn(dev, "%s: Illegal mode.\n", __func__);
}
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index 588e2d61c3b4..ab3428bf63fe 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -416,7 +416,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) ||
test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) {
/* Fill command header info */
- hdr = (struct fm_cmd_msg_hdr *)skb_put(skb, FM_CMD_MSG_HDR_SIZE);
+ hdr = skb_put(skb, FM_CMD_MSG_HDR_SIZE);
hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */
/* 3 (fm_opcode,rd_wr,dlen) + payload len) */
@@ -442,7 +442,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload,
fm_cb(skb)->fm_op = *((u8 *)payload + 2);
}
if (payload != NULL)
- memcpy(skb_put(skb, payload_len), payload, payload_len);
+ skb_put_data(skb, payload, payload_len);
fm_cb(skb)->completion = wait_completion;
skb_queue_tail(&fmdev->tx_q, skb);
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index e422f3d56f76..5e83b76495f7 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -169,11 +169,11 @@ config IR_HIX5HD2
tristate "Hisilicon hix5hd2 IR remote control"
depends on RC_CORE
help
- Say Y here if you want to use hisilicon hix5hd2 remote control.
- To compile this driver as a module, choose M here: the module will be
- called ir-hix5hd2.
+ Say Y here if you want to use hisilicon hix5hd2 remote control.
+ To compile this driver as a module, choose M here: the module will be
+ called ir-hix5hd2.
- If you're not sure, select N here
+ If you're not sure, select N here
config IR_IMON
tristate "SoundGraph iMON Receiver and Display"
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 9cf3e69de16a..a4c6ad4f67c1 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -904,9 +904,6 @@ static int ati_remote_probe(struct usb_interface *interface,
if (err)
goto exit_kill_urbs;
- /* use our delay for rc_dev */
- ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
-
/* Set up and register mouse input device */
if (mouse) {
input_dev = input_allocate_device();
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index ccf24fd7ec1b..8711a7ff55cc 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -113,6 +113,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
break;
case CMD_TX_OVERFLOW:
ir->tx_overflow = true;
+ /* fall through */
case CMD_RECEIVER_OFF:
case CMD_RECEIVER_ON:
case CMD_SEND:
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 431d33b36fb0..8d1439622533 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -702,10 +702,6 @@ static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto)
{
struct rc_dev *rdev = priv->hw.rdev;
- spin_lock_irq(&rdev->rc_map.lock);
- rdev->rc_map.rc_type = __ffs64(proto);
- spin_unlock_irq(&rdev->rc_map.lock);
-
mutex_lock(&rdev->lock);
rdev->enabled_protocols = proto;
rdev->allowed_wakeup_protocols = proto;
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 3489010601b5..bd76534a2749 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1722,7 +1722,7 @@ static void imon_incoming_scancode(struct imon_context *ictx,
if (kc == KEY_KEYBOARD && !ictx->release_code) {
ictx->last_keycode = kc;
if (!nomouse) {
- ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
+ ictx->pad_mouse = !ictx->pad_mouse;
dev_dbg(dev, "toggling to %s mode\n",
ictx->pad_mouse ? "mouse" : "keyboard");
spin_unlock_irqrestore(&ictx->kc_lock, flags);
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index de85f1d7ce43..d2223c04e9ad 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -266,7 +266,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
if (!dev->rx_resolution)
return -ENOTTY;
- val = dev->rx_resolution;
+ val = dev->rx_resolution / 1000;
break;
case LIRC_SET_WIDEBAND_RECEIVER:
@@ -327,16 +327,6 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
return ret;
}
-static int ir_lirc_open(void *data)
-{
- return 0;
-}
-
-static void ir_lirc_close(void *data)
-{
- return;
-}
-
static const struct file_operations lirc_fops = {
.owner = THIS_MODULE,
.write = ir_lirc_transmit_ir,
@@ -354,7 +344,6 @@ static const struct file_operations lirc_fops = {
static int ir_lirc_register(struct rc_dev *dev)
{
struct lirc_driver *drv;
- struct lirc_buffer *rbuf;
int rc = -ENOMEM;
unsigned long features = 0;
@@ -362,19 +351,12 @@ static int ir_lirc_register(struct rc_dev *dev)
if (!drv)
return rc;
- rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!rbuf)
- goto rbuf_alloc_failed;
-
- rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE);
- if (rc)
- goto rbuf_init_failed;
-
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
features |= LIRC_CAN_REC_MODE2;
if (dev->rx_resolution)
features |= LIRC_CAN_GET_REC_RESOLUTION;
}
+
if (dev->tx_ir) {
features |= LIRC_CAN_SEND_PULSE;
if (dev->s_tx_mask)
@@ -403,10 +385,10 @@ static int ir_lirc_register(struct rc_dev *dev)
drv->minor = -1;
drv->features = features;
drv->data = &dev->raw->lirc;
- drv->rbuf = rbuf;
- drv->set_use_inc = &ir_lirc_open;
- drv->set_use_dec = &ir_lirc_close;
+ drv->rbuf = NULL;
drv->code_length = sizeof(struct ir_raw_event) * 8;
+ drv->chunk_size = sizeof(int);
+ drv->buffer_size = LIRCBUF_SIZE;
drv->fops = &lirc_fops;
drv->dev = &dev->dev;
drv->rdev = dev;
@@ -415,19 +397,15 @@ static int ir_lirc_register(struct rc_dev *dev)
drv->minor = lirc_register_driver(drv);
if (drv->minor < 0) {
rc = -ENODEV;
- goto lirc_register_failed;
+ goto out;
}
dev->raw->lirc.drv = drv;
dev->raw->lirc.dev = dev;
return 0;
-lirc_register_failed:
-rbuf_init_failed:
- kfree(rbuf);
-rbuf_alloc_failed:
+out:
kfree(drv);
-
return rc;
}
@@ -436,9 +414,8 @@ static int ir_lirc_unregister(struct rc_dev *dev)
struct lirc_codec *lirc = &dev->raw->lirc;
lirc_unregister_driver(lirc->drv->minor);
- lirc_buffer_free(lirc->drv->rbuf);
- kfree(lirc->drv->rbuf);
kfree(lirc->drv);
+ lirc->drv = NULL;
return 0;
}
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index c8863f36686a..7e383b3fedd5 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -57,10 +57,13 @@ static int ir_spi_tx(struct rc_dev *dev,
/* convert the pulse/space signal to raw binary signal */
for (i = 0; i < count; i++) {
+ unsigned int periods;
int j;
- u16 val = ((i + 1) % 2) ? idata->pulse : idata->space;
+ u16 val;
- if (len + buffer[i] >= IR_SPI_MAX_BUFSIZE)
+ periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000);
+
+ if (len + periods >= IR_SPI_MAX_BUFSIZE)
return -EINVAL;
/*
@@ -69,13 +72,13 @@ static int ir_spi_tx(struct rc_dev *dev,
* contain a space duration.
*/
val = (i % 2) ? idata->space : idata->pulse;
- for (j = 0; j < buffer[i]; j++)
+ for (j = 0; j < periods; j++)
idata->tx_buf[len++] = val;
}
memset(&xfer, 0, sizeof(xfer));
- xfer.speed_hz = idata->freq;
+ xfer.speed_hz = idata->freq * 16;
xfer.len = len * sizeof(*idata->tx_buf);
xfer.tx_buf = idata->tx_buf;
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 8d60c9f00df9..db1e7b70c998 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -18,18 +18,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/sched/signal.h>
-#include <linux/errno.h>
#include <linux/ioctl.h>
-#include <linux/fs.h>
#include <linux/poll.h>
-#include <linux/completion.h>
#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/unistd.h>
-#include <linux/kthread.h>
-#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/cdev.h>
@@ -37,9 +29,6 @@
#include <media/lirc.h>
#include <media/lirc_dev.h>
-static bool debug;
-
-#define IRCTL_DEV_NAME "BaseRemoteCtl"
#define NOPLUG -1
#define LOGHEAD "lirc_dev (%s[%d]): "
@@ -52,13 +41,11 @@ struct irctl {
struct mutex irctl_lock;
struct lirc_buffer *buf;
+ bool buf_internal;
unsigned int chunk_size;
struct device dev;
struct cdev cdev;
-
- struct task_struct *task;
- long jiffies_to_wait;
};
static DEFINE_MUTEX(lirc_dev_lock);
@@ -68,22 +55,11 @@ static struct irctl *irctls[MAX_IRCTL_DEVICES];
/* Only used for sysfs but defined to void otherwise */
static struct class *lirc_class;
-/* helper function
- * initializes the irctl structure
- */
-static void lirc_irctl_init(struct irctl *ir)
-{
- mutex_init(&ir->irctl_lock);
- ir->d.minor = NOPLUG;
-}
-
static void lirc_release(struct device *ld)
{
struct irctl *ir = container_of(ld, struct irctl, dev);
- put_device(ir->dev.parent);
-
- if (ir->buf != ir->d.rbuf) {
+ if (ir->buf_internal) {
lirc_buffer_free(ir->buf);
kfree(ir->buf);
}
@@ -94,93 +70,6 @@ static void lirc_release(struct device *ld)
kfree(ir);
}
-/* helper function
- * reads key codes from driver and puts them into buffer
- * returns 0 on success
- */
-static int lirc_add_to_buf(struct irctl *ir)
-{
- int res;
- int got_data = -1;
-
- if (!ir->d.add_to_buf)
- return 0;
-
- /*
- * service the device as long as it is returning
- * data and we have space
- */
- do {
- got_data++;
- res = ir->d.add_to_buf(ir->d.data, ir->buf);
- } while (!res);
-
- if (res == -ENODEV)
- kthread_stop(ir->task);
-
- return got_data ? 0 : res;
-}
-
-/* main function of the polling thread
- */
-static int lirc_thread(void *irctl)
-{
- struct irctl *ir = irctl;
-
- do {
- if (ir->open) {
- if (ir->jiffies_to_wait) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(ir->jiffies_to_wait);
- }
- if (kthread_should_stop())
- break;
- if (!lirc_add_to_buf(ir))
- wake_up_interruptible(&ir->buf->wait_poll);
- } else {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- } while (!kthread_should_stop());
-
- return 0;
-}
-
-
-static const struct file_operations lirc_dev_fops = {
- .owner = THIS_MODULE,
- .read = lirc_dev_fop_read,
- .write = lirc_dev_fop_write,
- .poll = lirc_dev_fop_poll,
- .unlocked_ioctl = lirc_dev_fop_ioctl,
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
- .llseek = noop_llseek,
-};
-
-static int lirc_cdev_add(struct irctl *ir)
-{
- struct lirc_driver *d = &ir->d;
- struct cdev *cdev;
- int retval;
-
- cdev = &ir->cdev;
-
- if (d->fops) {
- cdev_init(cdev, d->fops);
- cdev->owner = d->owner;
- } else {
- cdev_init(cdev, &lirc_dev_fops);
- cdev->owner = THIS_MODULE;
- }
- retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
- if (retval)
- return retval;
-
- cdev->kobj.parent = &ir->dev.kobj;
- return cdev_add(cdev, ir->dev.devt, 1);
-}
-
static int lirc_allocate_buffer(struct irctl *ir)
{
int err = 0;
@@ -189,8 +78,6 @@ static int lirc_allocate_buffer(struct irctl *ir)
unsigned int buffer_size;
struct lirc_driver *d = &ir->d;
- mutex_lock(&lirc_dev_lock);
-
bytes_in_key = BITS_TO_LONGS(d->code_length) +
(d->code_length % 8 ? 1 : 0);
buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
@@ -198,6 +85,7 @@ static int lirc_allocate_buffer(struct irctl *ir)
if (d->rbuf) {
ir->buf = d->rbuf;
+ ir->buf_internal = false;
} else {
ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!ir->buf) {
@@ -208,18 +96,20 @@ static int lirc_allocate_buffer(struct irctl *ir)
err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
if (err) {
kfree(ir->buf);
+ ir->buf = NULL;
goto out;
}
+
+ ir->buf_internal = true;
+ d->rbuf = ir->buf;
}
ir->chunk_size = ir->buf->chunk_size;
out:
- mutex_unlock(&lirc_dev_lock);
-
return err;
}
-static int lirc_allocate_driver(struct lirc_driver *d)
+int lirc_register_driver(struct lirc_driver *d)
{
struct irctl *ir;
int minor;
@@ -235,6 +125,11 @@ static int lirc_allocate_driver(struct lirc_driver *d)
return -EINVAL;
}
+ if (!d->fops) {
+ pr_err("fops pointer not filled in!\n");
+ return -EINVAL;
+ }
+
if (d->minor >= MAX_IRCTL_DEVICES) {
dev_err(d->dev, "minor must be between 0 and %d!\n",
MAX_IRCTL_DEVICES - 1);
@@ -247,18 +142,8 @@ static int lirc_allocate_driver(struct lirc_driver *d)
return -EBADRQC;
}
- if (d->sample_rate) {
- if (2 > d->sample_rate || HZ < d->sample_rate) {
- dev_err(d->dev, "invalid %d sample rate\n",
- d->sample_rate);
- return -EBADRQC;
- }
- if (!d->add_to_buf) {
- dev_err(d->dev, "add_to_buf not set\n");
- return -EBADRQC;
- }
- } else if (!d->rbuf && !(d->fops && d->fops->read &&
- d->fops->poll && d->fops->unlocked_ioctl)) {
+ if (!d->rbuf && !(d->fops && d->fops->read &&
+ d->fops->poll && d->fops->unlocked_ioctl)) {
dev_err(d->dev, "undefined read, poll, ioctl\n");
return -EBADRQC;
}
@@ -288,7 +173,8 @@ static int lirc_allocate_driver(struct lirc_driver *d)
err = -ENOMEM;
goto out_lock;
}
- lirc_irctl_init(ir);
+
+ mutex_init(&ir->irctl_lock);
irctls[minor] = ir;
d->minor = minor;
@@ -300,32 +186,29 @@ static int lirc_allocate_driver(struct lirc_driver *d)
ir->d = *d;
+ if (LIRC_CAN_REC(d->features)) {
+ err = lirc_allocate_buffer(irctls[minor]);
+ if (err) {
+ kfree(ir);
+ goto out_lock;
+ }
+ d->rbuf = ir->buf;
+ }
+
+ device_initialize(&ir->dev);
ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);
ir->dev.class = lirc_class;
ir->dev.parent = d->dev;
ir->dev.release = lirc_release;
dev_set_name(&ir->dev, "lirc%d", ir->d.minor);
- device_initialize(&ir->dev);
- if (d->sample_rate) {
- ir->jiffies_to_wait = HZ / d->sample_rate;
+ cdev_init(&ir->cdev, d->fops);
+ ir->cdev.owner = ir->d.owner;
+ ir->cdev.kobj.parent = &ir->dev.kobj;
- /* try to fire up polling thread */
- ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
- if (IS_ERR(ir->task)) {
- dev_err(d->dev, "cannot run thread for minor = %d\n",
- d->minor);
- err = -ECHILD;
- goto out_sysfs;
- }
- } else {
- /* it means - wait for external event in task queue */
- ir->jiffies_to_wait = 0;
- }
-
- err = lirc_cdev_add(ir);
+ err = cdev_add(&ir->cdev, ir->dev.devt, 1);
if (err)
- goto out_sysfs;
+ goto out_free_dev;
ir->attached = 1;
@@ -335,37 +218,20 @@ static int lirc_allocate_driver(struct lirc_driver *d)
mutex_unlock(&lirc_dev_lock);
- get_device(ir->dev.parent);
-
dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
ir->d.name, ir->d.minor);
+
return minor;
+
out_cdev:
cdev_del(&ir->cdev);
-out_sysfs:
+out_free_dev:
put_device(&ir->dev);
out_lock:
mutex_unlock(&lirc_dev_lock);
return err;
}
-
-int lirc_register_driver(struct lirc_driver *d)
-{
- int minor, err = 0;
-
- minor = lirc_allocate_driver(d);
- if (minor < 0)
- return minor;
-
- if (LIRC_CAN_REC(d->features)) {
- err = lirc_allocate_buffer(irctls[minor]);
- if (err)
- lirc_unregister_driver(minor);
- }
-
- return err ? err : minor;
-}
EXPORT_SYMBOL(lirc_register_driver);
int lirc_unregister_driver(int minor)
@@ -393,10 +259,6 @@ int lirc_unregister_driver(int minor)
return -ENOENT;
}
- /* end up polling thread */
- if (ir->task)
- kthread_stop(ir->task);
-
dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
ir->d.name, ir->d.minor);
@@ -407,12 +269,6 @@ int lirc_unregister_driver(int minor)
wake_up_interruptible(&ir->buf->wait_poll);
}
- mutex_lock(&ir->irctl_lock);
-
- if (ir->d.set_use_dec)
- ir->d.set_use_dec(ir->d.data);
-
- mutex_unlock(&ir->irctl_lock);
mutex_unlock(&lirc_dev_lock);
device_del(&ir->dev);
@@ -462,17 +318,10 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
goto error;
}
+ if (ir->buf)
+ lirc_buffer_clear(ir->buf);
+
ir->open++;
- if (ir->d.set_use_inc)
- retval = ir->d.set_use_inc(ir->d.data);
- if (retval) {
- ir->open--;
- } else {
- if (ir->buf)
- lirc_buffer_clear(ir->buf);
- if (ir->task)
- wake_up_process(ir->task);
- }
error:
nonseekable_open(inode, file);
@@ -497,8 +346,6 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
rc_close(ir->d.rdev);
ir->open--;
- if (ir->d.set_use_dec)
- ir->d.set_use_dec(ir->d.data);
if (!ret)
mutex_unlock(&lirc_dev_lock);
@@ -517,7 +364,7 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
}
if (!ir->attached)
- return POLLERR;
+ return POLLHUP | POLLERR;
if (ir->buf) {
poll_wait(file, &ir->buf->wait_poll, wait);
@@ -729,24 +576,6 @@ void *lirc_get_pdata(struct file *file)
EXPORT_SYMBOL(lirc_get_pdata);
-ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
- size_t length, loff_t *ppos)
-{
- struct irctl *ir = irctls[iminor(file_inode(file))];
-
- if (!ir) {
- pr_err("called with invalid irctl\n");
- return -ENODEV;
- }
-
- if (!ir->attached)
- return -ENODEV;
-
- return -EINVAL;
-}
-EXPORT_SYMBOL(lirc_dev_fop_write);
-
-
static int __init lirc_dev_init(void)
{
int retval;
@@ -758,7 +587,7 @@ static int __init lirc_dev_init(void)
}
retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
- IRCTL_DEV_NAME);
+ "BaseRemoteCtl");
if (retval) {
class_destroy(lirc_class);
pr_err("alloc_chrdev_region failed\n");
@@ -784,6 +613,3 @@ module_exit(lirc_dev_exit);
MODULE_DESCRIPTION("LIRC base driver module");
MODULE_AUTHOR("Artur Lipowski");
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 93b16fe3ab38..eb130694bbb8 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -36,18 +36,18 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/pm_wakeup.h>
#include <media/rc-core.h>
-#define DRIVER_VERSION "1.92"
+#define DRIVER_VERSION "1.93"
#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
"device driver"
#define DRIVER_NAME "mceusb"
-#define USB_BUFLEN 32 /* USB reception buffer length */
#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */
#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */
@@ -417,7 +417,9 @@ struct mceusb_dev {
/* usb */
struct usb_device *usbdev;
struct urb *urb_in;
+ unsigned int pipe_in;
struct usb_endpoint_descriptor *usb_ep_out;
+ unsigned int pipe_out;
/* buffers and dma */
unsigned char *buf_in;
@@ -454,6 +456,16 @@ struct mceusb_dev {
u8 num_rxports; /* number of receive sensors */
u8 txports_cabled; /* bitmask of transmitters with cable */
u8 rxports_active; /* bitmask of active receive sensors */
+
+ /*
+ * support for async error handler mceusb_deferred_kevent()
+ * where usb_clear_halt(), usb_reset_configuration(),
+ * usb_reset_device(), etc. must be done in process context
+ */
+ struct work_struct kevent;
+ unsigned long kevent_flags;
+# define EVENT_TX_HALT 0
+# define EVENT_RX_HALT 1
};
/* MCE Device Command Strings, generally a port and command pair */
@@ -527,7 +539,7 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd)
}
static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
- int offset, int len, bool out)
+ int buf_len, int offset, int len, bool out)
{
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
char *inout;
@@ -544,7 +556,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
return;
dev_dbg(dev, "%cx data: %*ph (length=%d)",
- (out ? 't' : 'r'), min(len, USB_BUFLEN), buf, len);
+ (out ? 't' : 'r'),
+ min(len, buf_len - offset), buf + offset, len);
inout = out ? "Request" : "Got";
@@ -686,6 +699,21 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
#endif
}
+/*
+ * Schedule work that can't be done in interrupt handlers
+ * (mceusb_dev_recv() and mce_async_callback()) nor tasklets.
+ * Invokes mceusb_deferred_kevent() for recovering from
+ * error events specified by the kevent bit field.
+ */
+static void mceusb_defer_kevent(struct mceusb_dev *ir, int kevent)
+{
+ set_bit(kevent, &ir->kevent_flags);
+ if (!schedule_work(&ir->kevent))
+ dev_err(ir->dev, "kevent %d may have been dropped", kevent);
+ else
+ dev_dbg(ir->dev, "kevent %d scheduled", kevent);
+}
+
static void mce_async_callback(struct urb *urb)
{
struct mceusb_dev *ir;
@@ -701,7 +729,8 @@ static void mce_async_callback(struct urb *urb)
case 0:
len = urb->actual_length;
- mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
+ mceusb_dev_printdata(ir, urb->transfer_buffer, len,
+ 0, len, true);
break;
case -ECONNRESET:
@@ -711,6 +740,11 @@ static void mce_async_callback(struct urb *urb)
break;
case -EPIPE:
+ dev_err(ir->dev, "Error: request urb status = %d (TX HALT)",
+ urb->status);
+ mceusb_defer_kevent(ir, EVENT_TX_HALT);
+ break;
+
default:
dev_err(ir->dev, "Error: request urb status = %d", urb->status);
break;
@@ -721,18 +755,18 @@ static void mce_async_callback(struct urb *urb)
usb_free_urb(urb);
}
-/* request incoming or send outgoing usb packet - used to initialize remote */
+/* request outgoing (send) usb packet - used to initialize remote */
static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
int size)
{
- int res, pipe;
+ int res;
struct urb *async_urb;
struct device *dev = ir->dev;
unsigned char *async_buf;
async_urb = usb_alloc_urb(0, GFP_KERNEL);
if (unlikely(!async_urb)) {
- dev_err(dev, "Error, couldn't allocate urb!\n");
+ dev_err(dev, "Error, couldn't allocate urb!");
return;
}
@@ -743,32 +777,26 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
}
/* outbound data */
- if (usb_endpoint_xfer_int(ir->usb_ep_out)) {
- pipe = usb_sndintpipe(ir->usbdev,
- ir->usb_ep_out->bEndpointAddress);
- usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf,
- size, mce_async_callback, ir,
+ if (usb_endpoint_xfer_int(ir->usb_ep_out))
+ usb_fill_int_urb(async_urb, ir->usbdev, ir->pipe_out,
+ async_buf, size, mce_async_callback, ir,
ir->usb_ep_out->bInterval);
- } else {
- pipe = usb_sndbulkpipe(ir->usbdev,
- ir->usb_ep_out->bEndpointAddress);
- usb_fill_bulk_urb(async_urb, ir->usbdev, pipe,
- async_buf, size, mce_async_callback,
- ir);
- }
- memcpy(async_buf, data, size);
+ else
+ usb_fill_bulk_urb(async_urb, ir->usbdev, ir->pipe_out,
+ async_buf, size, mce_async_callback, ir);
- dev_dbg(dev, "receive request called (size=%#x)", size);
+ memcpy(async_buf, data, size);
- async_urb->transfer_buffer_length = size;
- async_urb->dev = ir->usbdev;
+ dev_dbg(dev, "send request called (size=%#x)", size);
res = usb_submit_urb(async_urb, GFP_ATOMIC);
if (res) {
- dev_err(dev, "receive request FAILED! (res=%d)", res);
+ dev_err(dev, "send request FAILED! (res=%d)", res);
+ kfree(async_buf);
+ usb_free_urb(async_urb);
return;
}
- dev_dbg(dev, "receive request complete (res=%d)", res);
+ dev_dbg(dev, "send request complete (res=%d)", res);
}
static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
@@ -974,7 +1002,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
switch (ir->parser_state) {
case SUBCMD:
ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]);
- mceusb_dev_printdata(ir, ir->buf_in, i - 1,
+ mceusb_dev_printdata(ir, ir->buf_in, buf_len, i - 1,
ir->rem + 2, false);
mceusb_handle_command(ir, i);
ir->parser_state = CMD_DATA;
@@ -986,7 +1014,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
* US_TO_NS(MCE_TIME_UNIT);
- dev_dbg(ir->dev, "Storing %s with duration %d",
+ dev_dbg(ir->dev, "Storing %s with duration %u",
rawir.pulse ? "pulse" : "space",
rawir.duration);
@@ -1007,7 +1035,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
continue;
}
ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK);
- mceusb_dev_printdata(ir, ir->buf_in,
+ mceusb_dev_printdata(ir, ir->buf_in, buf_len,
i, ir->rem + 1, false);
if (ir->rem)
ir->parser_state = PARSE_IRDATA;
@@ -1052,6 +1080,11 @@ static void mceusb_dev_recv(struct urb *urb)
return;
case -EPIPE:
+ dev_err(ir->dev, "Error: urb status = %d (RX HALT)",
+ urb->status);
+ mceusb_defer_kevent(ir, EVENT_RX_HALT);
+ return;
+
default:
dev_err(ir->dev, "Error: urb status = %d", urb->status);
break;
@@ -1170,6 +1203,45 @@ static void mceusb_flash_led(struct mceusb_dev *ir)
mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
}
+/*
+ * Workqueue function
+ * for resetting or recovering device after occurrence of error events
+ * specified in ir->kevent bit field.
+ * Function runs (via schedule_work()) in non-interrupt context, for
+ * calls here (such as usb_clear_halt()) requiring non-interrupt context.
+ */
+static void mceusb_deferred_kevent(struct work_struct *work)
+{
+ struct mceusb_dev *ir =
+ container_of(work, struct mceusb_dev, kevent);
+ int status;
+
+ if (test_bit(EVENT_RX_HALT, &ir->kevent_flags)) {
+ usb_unlink_urb(ir->urb_in);
+ status = usb_clear_halt(ir->usbdev, ir->pipe_in);
+ if (status < 0) {
+ dev_err(ir->dev, "rx clear halt error %d",
+ status);
+ }
+ clear_bit(EVENT_RX_HALT, &ir->kevent_flags);
+ if (status == 0) {
+ status = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (status < 0) {
+ dev_err(ir->dev,
+ "rx unhalt submit urb error %d",
+ status);
+ }
+ }
+ }
+
+ if (test_bit(EVENT_TX_HALT, &ir->kevent_flags)) {
+ status = usb_clear_halt(ir->usbdev, ir->pipe_out);
+ if (status < 0)
+ dev_err(ir->dev, "tx clear halt error %d", status);
+ clear_bit(EVENT_TX_HALT, &ir->kevent_flags);
+ }
+}
+
static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
{
struct usb_device *udev = ir->usbdev;
@@ -1303,6 +1375,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
if (!ir)
goto mem_alloc_fail;
+ ir->pipe_in = pipe;
ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in);
if (!ir->buf_in)
goto buf_in_alloc_fail;
@@ -1321,6 +1394,12 @@ static int mceusb_dev_probe(struct usb_interface *intf,
/* Saving usb interface data for use by the transmitter routine */
ir->usb_ep_out = ep_out;
+ if (usb_endpoint_xfer_int(ep_out))
+ ir->pipe_out = usb_sndintpipe(ir->usbdev,
+ ep_out->bEndpointAddress);
+ else
+ ir->pipe_out = usb_sndbulkpipe(ir->usbdev,
+ ep_out->bEndpointAddress);
if (dev->descriptor.iManufacturer
&& usb_string(dev, dev->descriptor.iManufacturer,
@@ -1332,21 +1411,32 @@ static int mceusb_dev_probe(struct usb_interface *intf,
snprintf(name + strlen(name), sizeof(name) - strlen(name),
" %s", buf);
+ /*
+ * Initialize async USB error handler before registering
+ * or activating any mceusb RX and TX functions
+ */
+ INIT_WORK(&ir->kevent, mceusb_deferred_kevent);
+
ir->rc = mceusb_init_rc_dev(ir);
if (!ir->rc)
goto rc_dev_fail;
/* wire up inbound data handler */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
- mceusb_dev_recv, ir, ep_in->bInterval);
+ if (usb_endpoint_xfer_int(ep_in))
+ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir, ep_in->bInterval);
+ else
+ usb_fill_bulk_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir);
+
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* flush buffers on the device */
- dev_dbg(&intf->dev, "Flushing receive buffers\n");
+ dev_dbg(&intf->dev, "Flushing receive buffers");
res = usb_submit_urb(ir->urb_in, GFP_KERNEL);
if (res)
- dev_err(&intf->dev, "failed to flush buffers: %d\n", res);
+ dev_err(&intf->dev, "failed to flush buffers: %d", res);
/* figure out which firmware/emulator version this hardware has */
mceusb_get_emulator_version(ir);
@@ -1380,6 +1470,7 @@ 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);
@@ -1405,6 +1496,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
return;
ir->usbdev = NULL;
+ cancel_work_sync(&ir->kevent);
rc_unregister_device(ir->rc);
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
index 5576dbd6b1a4..65566d569cb1 100644
--- a/drivers/media/rc/meson-ir.c
+++ b/drivers/media/rc/meson-ir.c
@@ -19,6 +19,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
+#include <linux/bitfield.h>
#include <media/rc-core.h>
@@ -36,7 +37,7 @@
/* only available on Meson 8b and newer */
#define IR_DEC_REG2 0x20
-#define REG0_RATE_MASK (BIT(11) - 1)
+#define REG0_RATE_MASK GENMASK(11, 0)
#define DECODE_MODE_NEC 0x0
#define DECODE_MODE_RAW 0x2
@@ -49,14 +50,13 @@
#define REG2_MODE_MASK GENMASK(3, 0)
#define REG2_MODE_SHIFT 0
-#define REG1_TIME_IV_SHIFT 16
-#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
+#define REG1_TIME_IV_MASK GENMASK(28, 16)
-#define REG1_IRQSEL_MASK (BIT(2) | BIT(3))
-#define REG1_IRQSEL_NEC_MODE (0 << 2)
-#define REG1_IRQSEL_RISE_FALL (1 << 2)
-#define REG1_IRQSEL_FALL (2 << 2)
-#define REG1_IRQSEL_RISE (3 << 2)
+#define REG1_IRQSEL_MASK GENMASK(3, 2)
+#define REG1_IRQSEL_NEC_MODE 0
+#define REG1_IRQSEL_RISE_FALL 1
+#define REG1_IRQSEL_FALL 2
+#define REG1_IRQSEL_RISE 3
#define REG1_RESET BIT(0)
#define REG1_ENABLE BIT(15)
@@ -68,7 +68,6 @@
struct meson_ir {
void __iomem *reg;
struct rc_dev *rc;
- int irq;
spinlock_t lock;
};
@@ -86,18 +85,19 @@ static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
{
struct meson_ir *ir = dev_id;
- u32 duration;
+ u32 duration, status;
DEFINE_IR_RAW_EVENT(rawir);
spin_lock(&ir->lock);
- duration = readl(ir->reg + IR_DEC_REG1);
- duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT;
+ duration = readl_relaxed(ir->reg + IR_DEC_REG1);
+ duration = FIELD_GET(REG1_TIME_IV_MASK, duration);
rawir.duration = US_TO_NS(duration * MESON_TRATE);
- rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN);
+ status = readl_relaxed(ir->reg + IR_DEC_STATUS);
+ rawir.pulse = !!(status & STATUS_IR_DEC_IN);
- ir_raw_event_store_with_filter(ir->rc, &rawir);
+ ir_raw_event_store(ir->rc, &rawir);
ir_raw_event_handle(ir->rc);
spin_unlock(&ir->lock);
@@ -112,7 +112,7 @@ static int meson_ir_probe(struct platform_device *pdev)
struct resource *res;
const char *map_name;
struct meson_ir *ir;
- int ret;
+ int irq, ret;
ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL);
if (!ir)
@@ -125,13 +125,13 @@ static int meson_ir_probe(struct platform_device *pdev)
return PTR_ERR(ir->reg);
}
- ir->irq = platform_get_irq(pdev, 0);
- if (ir->irq < 0) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
dev_err(dev, "no irq resource\n");
- return ir->irq;
+ return irq;
}
- ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW);
+ ir->rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW);
if (!ir->rc) {
dev_err(dev, "failed to allocate rc device\n");
return -ENOMEM;
@@ -143,7 +143,6 @@ static int meson_ir_probe(struct platform_device *pdev)
ir->rc->input_id.bustype = BUS_HOST;
map_name = of_get_property(node, "linux,rc-map-name", NULL);
ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
- ir->rc->dev.parent = dev;
ir->rc->allowed_protocols = RC_BIT_ALL_IR_DECODER;
ir->rc->rx_resolution = US_TO_NS(MESON_TRATE);
ir->rc->timeout = MS_TO_NS(200);
@@ -152,16 +151,16 @@ static int meson_ir_probe(struct platform_device *pdev)
spin_lock_init(&ir->lock);
platform_set_drvdata(pdev, ir);
- ret = rc_register_device(ir->rc);
+ ret = devm_rc_register_device(dev, ir->rc);
if (ret) {
dev_err(dev, "failed to register rc device\n");
- goto out_free;
+ return ret;
}
- ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir);
+ ret = devm_request_irq(dev, irq, meson_ir_irq, 0, NULL, ir);
if (ret) {
dev_err(dev, "failed to request irq\n");
- goto out_unreg;
+ return ret;
}
/* Reset the decoder */
@@ -171,29 +170,22 @@ static int meson_ir_probe(struct platform_device *pdev)
/* Set general operation mode (= raw/software decoding) */
if (of_device_is_compatible(node, "amlogic,meson6-ir"))
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
- DECODE_MODE_RAW << REG1_MODE_SHIFT);
+ FIELD_PREP(REG1_MODE_MASK, DECODE_MODE_RAW));
else
meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
- DECODE_MODE_RAW << REG2_MODE_SHIFT);
+ FIELD_PREP(REG2_MODE_MASK, DECODE_MODE_RAW));
/* Set rate */
meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
/* IRQ on rising and falling edges */
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
- REG1_IRQSEL_RISE_FALL);
+ FIELD_PREP(REG1_IRQSEL_MASK, REG1_IRQSEL_RISE_FALL));
/* Enable the decoder */
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
dev_info(dev, "receiver initialized\n");
return 0;
-out_unreg:
- rc_unregister_device(ir->rc);
- ir->rc = NULL;
-out_free:
- rc_free_device(ir->rc);
-
- return ret;
}
static int meson_ir_remove(struct platform_device *pdev)
@@ -206,11 +198,35 @@ static int meson_ir_remove(struct platform_device *pdev)
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
spin_unlock_irqrestore(&ir->lock, flags);
- rc_unregister_device(ir->rc);
-
return 0;
}
+static void meson_ir_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct meson_ir *ir = platform_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ir->lock, flags);
+
+ /*
+ * Set operation mode to NEC/hardware decoding to give
+ * bootloader a chance to power the system back on
+ */
+ if (of_device_is_compatible(node, "amlogic,meson6-ir"))
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
+ DECODE_MODE_NEC << REG1_MODE_SHIFT);
+ else
+ meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
+ DECODE_MODE_NEC << REG2_MODE_SHIFT);
+
+ /* Set rate to default value */
+ meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, 0x13);
+
+ spin_unlock_irqrestore(&ir->lock, flags);
+}
+
static const struct of_device_id meson_ir_match[] = {
{ .compatible = "amlogic,meson6-ir" },
{ .compatible = "amlogic,meson8b-ir" },
@@ -222,6 +238,7 @@ MODULE_DEVICE_TABLE(of, meson_ir_match);
static struct platform_driver meson_ir_driver = {
.probe = meson_ir_probe,
.remove = meson_ir_remove,
+ .shutdown = meson_ir_shutdown,
.driver = {
.name = DRIVER_NAME,
.of_match_table = meson_ir_match,
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 0455b273c2fc..b3e7cac2c3ee 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -263,7 +263,9 @@ int ir_raw_gen_pl(struct ir_raw_event **ev, unsigned int max,
* Routines from rc-raw.c to be used internally and by decoders
*/
u64 ir_raw_get_allowed_protocols(void);
+int ir_raw_event_prepare(struct rc_dev *dev);
int ir_raw_event_register(struct rc_dev *dev);
+void ir_raw_event_free(struct rc_dev *dev);
void ir_raw_event_unregister(struct rc_dev *dev);
int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index a2fc1a1d58b0..b6d256f03847 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -486,15 +486,18 @@ EXPORT_SYMBOL(ir_raw_encode_scancode);
/*
* Used to (un)register raw event clients
*/
-int ir_raw_event_register(struct rc_dev *dev)
+int ir_raw_event_prepare(struct rc_dev *dev)
{
- int rc;
- struct ir_raw_handler *handler;
- struct task_struct *thread;
+ static bool raw_init; /* 'false' default value, raw decoders loaded? */
if (!dev)
return -EINVAL;
+ if (!raw_init) {
+ request_module("ir-lirc-codec");
+ raw_init = true;
+ }
+
dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL);
if (!dev->raw)
return -ENOMEM;
@@ -503,6 +506,14 @@ int ir_raw_event_register(struct rc_dev *dev)
dev->change_protocol = change_protocol;
INIT_KFIFO(dev->raw->kfifo);
+ return 0;
+}
+
+int ir_raw_event_register(struct rc_dev *dev)
+{
+ struct ir_raw_handler *handler;
+ struct task_struct *thread;
+
/*
* raw transmitters do not need any event registration
* because the event is coming from userspace
@@ -511,10 +522,8 @@ int ir_raw_event_register(struct rc_dev *dev)
thread = kthread_run(ir_raw_event_thread, dev->raw, "rc%u",
dev->minor);
- if (IS_ERR(thread)) {
- rc = PTR_ERR(thread);
- goto out;
- }
+ if (IS_ERR(thread))
+ return PTR_ERR(thread);
dev->raw->thread = thread;
}
@@ -527,11 +536,15 @@ int ir_raw_event_register(struct rc_dev *dev)
mutex_unlock(&ir_raw_handler_lock);
return 0;
+}
+
+void ir_raw_event_free(struct rc_dev *dev)
+{
+ if (!dev)
+ return;
-out:
kfree(dev->raw);
dev->raw = NULL;
- return rc;
}
void ir_raw_event_unregister(struct rc_dev *dev)
@@ -550,8 +563,7 @@ void ir_raw_event_unregister(struct rc_dev *dev)
handler->raw_unregister(dev);
mutex_unlock(&ir_raw_handler_lock);
- kfree(dev->raw);
- dev->raw = NULL;
+ ir_raw_event_free(dev);
}
/*
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 6ec73357fa47..a9eba0013525 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -15,7 +15,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <media/rc-core.h>
-#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/input.h>
@@ -934,8 +933,8 @@ static bool lirc_is_present(void)
* It returns the protocol names of supported protocols.
* Enabled protocols are printed in brackets.
*
- * dev->lock is taken to guard against races between device
- * registration, store_protocols and show_protocols.
+ * dev->lock is taken to guard against races between
+ * store_protocols and show_protocols.
*/
static ssize_t show_protocols(struct device *device,
struct device_attribute *mattr, char *buf)
@@ -945,13 +944,6 @@ static ssize_t show_protocols(struct device *device,
char *tmp = buf;
int i;
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
mutex_lock(&dev->lock);
enabled = dev->enabled_protocols;
@@ -1106,8 +1098,8 @@ static void ir_raw_load_modules(u64 *protocols)
* See parse_protocol_change() for the valid commands.
* Returns @len on success or a negative error code.
*
- * dev->lock is taken to guard against races between device
- * registration, store_protocols and show_protocols.
+ * dev->lock is taken to guard against races between
+ * store_protocols and show_protocols.
*/
static ssize_t store_protocols(struct device *device,
struct device_attribute *mattr,
@@ -1119,13 +1111,6 @@ static ssize_t store_protocols(struct device *device,
u64 old_protocols, new_protocols;
ssize_t rc;
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
IR_dprintk(1, "Normal protocol change requested\n");
current_protocols = &dev->enabled_protocols;
filter = &dev->scancode_filter;
@@ -1200,7 +1185,7 @@ out:
* Bits of the filter value corresponding to set bits in the filter mask are
* compared against input scancodes and non-matching scancodes are discarded.
*
- * dev->lock is taken to guard against races between device registration,
+ * dev->lock is taken to guard against races between
* store_filter and show_filter.
*/
static ssize_t show_filter(struct device *device,
@@ -1212,13 +1197,6 @@ static ssize_t show_filter(struct device *device,
struct rc_scancode_filter *filter;
u32 val;
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
mutex_lock(&dev->lock);
if (fattr->type == RC_FILTER_NORMAL)
@@ -1251,7 +1229,7 @@ static ssize_t show_filter(struct device *device,
* Bits of the filter value corresponding to set bits in the filter mask are
* compared against input scancodes and non-matching scancodes are discarded.
*
- * dev->lock is taken to guard against races between device registration,
+ * dev->lock is taken to guard against races between
* store_filter and show_filter.
*/
static ssize_t store_filter(struct device *device,
@@ -1265,13 +1243,6 @@ static ssize_t store_filter(struct device *device,
unsigned long val;
int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
ret = kstrtoul(buf, 0, &val);
if (ret < 0)
return ret;
@@ -1372,8 +1343,8 @@ static const char * const proto_variant_names[] = {
* It returns the protocol names of supported protocols.
* The enabled protocols are printed in brackets.
*
- * dev->lock is taken to guard against races between device
- * registration, store_protocols and show_protocols.
+ * dev->lock is taken to guard against races between
+ * store_wakeup_protocols and show_wakeup_protocols.
*/
static ssize_t show_wakeup_protocols(struct device *device,
struct device_attribute *mattr,
@@ -1385,13 +1356,6 @@ static ssize_t show_wakeup_protocols(struct device *device,
char *tmp = buf;
int i;
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
mutex_lock(&dev->lock);
allowed = dev->allowed_wakeup_protocols;
@@ -1431,8 +1395,8 @@ static ssize_t show_wakeup_protocols(struct device *device,
* It is trigged by writing to /sys/class/rc/rc?/wakeup_protocols.
* Returns @len on success or a negative error code.
*
- * dev->lock is taken to guard against races between device
- * registration, store_protocols and show_protocols.
+ * dev->lock is taken to guard against races between
+ * store_wakeup_protocols and show_wakeup_protocols.
*/
static ssize_t store_wakeup_protocols(struct device *device,
struct device_attribute *mattr,
@@ -1444,13 +1408,6 @@ static ssize_t store_wakeup_protocols(struct device *device,
u64 allowed;
int i;
- /* Device is being removed */
- if (!dev)
- return -EINVAL;
-
- if (!atomic_read(&dev->initialized))
- return -ERESTARTSYS;
-
mutex_lock(&dev->lock);
allowed = dev->allowed_wakeup_protocols;
@@ -1663,7 +1620,7 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_rc_allocate_device);
-static int rc_setup_rx_device(struct rc_dev *dev)
+static int rc_prepare_rx_device(struct rc_dev *dev)
{
int rc;
struct rc_map *rc_map;
@@ -1703,6 +1660,28 @@ static int rc_setup_rx_device(struct rc_dev *dev)
if (dev->close)
dev->input_dev->close = ir_close;
+ dev->input_dev->dev.parent = &dev->dev;
+ memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
+ dev->input_dev->phys = dev->input_phys;
+ dev->input_dev->name = dev->input_name;
+
+ return 0;
+
+out_table:
+ ir_free_table(&dev->rc_map);
+
+ return rc;
+}
+
+static int rc_setup_rx_device(struct rc_dev *dev)
+{
+ int rc;
+
+ /* rc_open will be called here */
+ rc = input_register_device(dev->input_dev);
+ if (rc)
+ return rc;
+
/*
* Default delay of 250ms is too short for some protocols, especially
* since the timeout is currently set to 250ms. Increase it to 500ms,
@@ -1718,38 +1697,24 @@ static int rc_setup_rx_device(struct rc_dev *dev)
*/
dev->input_dev->rep[REP_PERIOD] = 125;
- dev->input_dev->dev.parent = &dev->dev;
- memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
- dev->input_dev->phys = dev->input_phys;
- dev->input_dev->name = dev->input_name;
-
- /* rc_open will be called here */
- rc = input_register_device(dev->input_dev);
- if (rc)
- goto out_table;
-
return 0;
-
-out_table:
- ir_free_table(&dev->rc_map);
-
- return rc;
}
static void rc_free_rx_device(struct rc_dev *dev)
{
- if (!dev || dev->driver_type == RC_DRIVER_IR_RAW_TX)
+ if (!dev)
return;
- ir_free_table(&dev->rc_map);
+ if (dev->input_dev) {
+ input_unregister_device(dev->input_dev);
+ dev->input_dev = NULL;
+ }
- input_unregister_device(dev->input_dev);
- dev->input_dev = NULL;
+ ir_free_table(&dev->rc_map);
}
int rc_register_device(struct rc_dev *dev)
{
- static bool raw_init; /* 'false' default value, raw decoders loaded? */
const char *path;
int attr = 0;
int minor;
@@ -1765,7 +1730,6 @@ int rc_register_device(struct rc_dev *dev)
dev->minor = minor;
dev_set_name(&dev->dev, "rc%u", dev->minor);
dev_set_drvdata(&dev->dev, dev);
- atomic_set(&dev->initialized, 0);
dev->dev.groups = dev->sysfs_groups;
if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
@@ -1776,34 +1740,40 @@ int rc_register_device(struct rc_dev *dev)
dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
dev->sysfs_groups[attr++] = NULL;
+ if (dev->driver_type == RC_DRIVER_IR_RAW ||
+ dev->driver_type == RC_DRIVER_IR_RAW_TX) {
+ rc = ir_raw_event_prepare(dev);
+ if (rc < 0)
+ goto out_minor;
+ }
+
+ if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
+ rc = rc_prepare_rx_device(dev);
+ if (rc)
+ goto out_raw;
+ }
+
rc = device_add(&dev->dev);
if (rc)
- goto out_unlock;
+ goto out_rx_free;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
dev_info(&dev->dev, "%s as %s\n",
dev->input_name ?: "Unspecified device", path ?: "N/A");
kfree(path);
- if (dev->driver_type == RC_DRIVER_IR_RAW ||
- dev->driver_type == RC_DRIVER_IR_RAW_TX) {
- if (!raw_init) {
- request_module_nowait("ir-lirc-codec");
- raw_init = true;
- }
- rc = ir_raw_event_register(dev);
- if (rc < 0)
- goto out_dev;
- }
-
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
rc = rc_setup_rx_device(dev);
if (rc)
- goto out_raw;
+ goto out_dev;
}
- /* Allow the RC sysfs nodes to be accessible */
- atomic_set(&dev->initialized, 1);
+ if (dev->driver_type == RC_DRIVER_IR_RAW ||
+ dev->driver_type == RC_DRIVER_IR_RAW_TX) {
+ rc = ir_raw_event_register(dev);
+ if (rc < 0)
+ goto out_rx;
+ }
IR_dprintk(1, "Registered rc%u (driver: %s)\n",
dev->minor,
@@ -1811,11 +1781,15 @@ int rc_register_device(struct rc_dev *dev)
return 0;
-out_raw:
- ir_raw_event_unregister(dev);
+out_rx:
+ rc_free_rx_device(dev);
out_dev:
device_del(&dev->dev);
-out_unlock:
+out_rx_free:
+ ir_free_table(&dev->rc_map);
+out_raw:
+ ir_raw_event_free(dev);
+out_minor:
ida_simple_remove(&rc_ida, minor);
return rc;
}
diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c
index 90a5f8fd5eea..20234ba0b318 100644
--- a/drivers/media/rc/sir_ir.c
+++ b/drivers/media/rc/sir_ir.c
@@ -53,16 +53,13 @@ static DEFINE_SPINLOCK(hardware_lock);
/* Communication with user-space */
static void add_read_queue(int flag, unsigned long val);
-static int init_chrdev(void);
/* Hardware */
static irqreturn_t sir_interrupt(int irq, void *dev_id);
static void send_space(unsigned long len);
static void send_pulse(unsigned long len);
-static int init_hardware(void);
+static void init_hardware(void);
static void drop_hardware(void);
/* Initialisation */
-static int init_port(void);
-static void drop_port(void);
static inline unsigned int sinp(int offset)
{
@@ -122,28 +119,6 @@ static void add_read_queue(int flag, unsigned long val)
ir_raw_event_store_with_filter(rcdev, &ev);
}
-static int init_chrdev(void)
-{
- rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW);
- if (!rcdev)
- return -ENOMEM;
-
- rcdev->input_name = "SIR IrDA port";
- rcdev->input_phys = KBUILD_MODNAME "/input0";
- rcdev->input_id.bustype = BUS_HOST;
- rcdev->input_id.vendor = 0x0001;
- rcdev->input_id.product = 0x0001;
- rcdev->input_id.version = 0x0100;
- rcdev->tx_ir = sir_tx_ir;
- rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
- rcdev->driver_name = KBUILD_MODNAME;
- rcdev->map_name = RC_MAP_RC6_MCE;
- rcdev->timeout = IR_DEFAULT_TIMEOUT;
- rcdev->dev.parent = &sir_ir_dev->dev;
-
- return devm_rc_register_device(&sir_ir_dev->dev, rcdev);
-}
-
/* SECTION: Hardware */
static void sir_timeout(unsigned long data)
{
@@ -288,7 +263,7 @@ static void send_pulse(unsigned long len)
}
}
-static int init_hardware(void)
+static void init_hardware(void)
{
unsigned long flags;
@@ -310,7 +285,6 @@ static int init_hardware(void)
/* turn on UART */
outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR);
spin_unlock_irqrestore(&hardware_lock, flags);
- return 0;
}
static void drop_hardware(void)
@@ -326,61 +300,55 @@ static void drop_hardware(void)
}
/* SECTION: Initialisation */
-
-static int init_port(void)
+static int sir_ir_probe(struct platform_device *dev)
{
int retval;
+ rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW);
+ if (!rcdev)
+ return -ENOMEM;
+
+ rcdev->input_name = "SIR IrDA port";
+ rcdev->input_phys = KBUILD_MODNAME "/input0";
+ rcdev->input_id.bustype = BUS_HOST;
+ rcdev->input_id.vendor = 0x0001;
+ rcdev->input_id.product = 0x0001;
+ rcdev->input_id.version = 0x0100;
+ rcdev->tx_ir = sir_tx_ir;
+ rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
+ rcdev->driver_name = KBUILD_MODNAME;
+ rcdev->map_name = RC_MAP_RC6_MCE;
+ rcdev->timeout = IR_DEFAULT_TIMEOUT;
+ rcdev->dev.parent = &sir_ir_dev->dev;
+
setup_timer(&timerlist, sir_timeout, 0);
/* get I/O port access and IRQ line */
- if (!request_region(io, 8, KBUILD_MODNAME)) {
+ if (!devm_request_region(&sir_ir_dev->dev, io, 8, KBUILD_MODNAME)) {
pr_err("i/o port 0x%.4x already in use.\n", io);
return -EBUSY;
}
- retval = request_irq(irq, sir_interrupt, 0,
- KBUILD_MODNAME, NULL);
+ retval = devm_request_irq(&sir_ir_dev->dev, irq, sir_interrupt, 0,
+ KBUILD_MODNAME, NULL);
if (retval < 0) {
- release_region(io, 8);
pr_err("IRQ %d already in use.\n", irq);
return retval;
}
pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq);
- return 0;
-}
-
-static void drop_port(void)
-{
- free_irq(irq, NULL);
- del_timer_sync(&timerlist);
- release_region(io, 8);
-}
-
-static int init_sir_ir(void)
-{
- int retval;
-
- retval = init_port();
+ retval = devm_rc_register_device(&sir_ir_dev->dev, rcdev);
if (retval < 0)
return retval;
- init_hardware();
- return 0;
-}
-
-static int sir_ir_probe(struct platform_device *dev)
-{
- int retval;
- retval = init_chrdev();
- if (retval < 0)
- return retval;
+ init_hardware();
- return init_sir_ir();
+ return 0;
}
static int sir_ir_remove(struct platform_device *dev)
{
+ drop_hardware();
+ del_timer_sync(&timerlist);
return 0;
}
@@ -421,8 +389,6 @@ pdev_alloc_fail:
static void __exit sir_ir_exit(void)
{
- drop_hardware();
- drop_port();
platform_device_unregister(sir_ir_dev);
platform_driver_unregister(&sir_ir_driver);
}
@@ -434,10 +400,10 @@ MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
MODULE_AUTHOR("Milan Pikula");
MODULE_LICENSE("GPL");
-module_param(io, int, 0444);
+module_param_hw(io, int, ioport, 0444);
MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
module_param(threshold, int, 0444);
diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c
index 192b1c7740df..145407dee3db 100644
--- a/drivers/media/tuners/fc0011.c
+++ b/drivers/media/tuners/fc0011.c
@@ -342,6 +342,7 @@ static int fc0011_set_params(struct dvb_frontend *fe)
switch (vco_sel) {
default:
WARN_ON(1);
+ return -EINVAL;
case 0:
if (vco_cal < 8) {
regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c
index 353744fee053..dd59c2c0e4a5 100644
--- a/drivers/media/tuners/mxl5005s.c
+++ b/drivers/media/tuners/mxl5005s.c
@@ -2737,8 +2737,6 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7);
divider_val = 2 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
}
/* TG_DIV_VAL */
diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c
index b4e5fa2ff5e5..147155553648 100644
--- a/drivers/media/tuners/tda18271-fe.c
+++ b/drivers/media/tuners/tda18271-fe.c
@@ -960,7 +960,7 @@ static int tda18271_set_params(struct dvb_frontend *fe)
break;
case SYS_DVBC_ANNEX_B:
bw = 6000000;
- /* falltrough */
+ /* fall through */
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
if (bw <= 6000000) {
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
index e823aafce276..0e7e4fdf9e50 100644
--- a/drivers/media/tuners/xc5000.c
+++ b/drivers/media/tuners/xc5000.c
@@ -565,38 +565,16 @@ static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain)
return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain);
}
-static u16 wait_for_lock(struct xc5000_priv *priv)
-{
- u16 lock_state = 0;
- int watch_dog_count = 40;
-
- while ((lock_state == 0) && (watch_dog_count > 0)) {
- xc_get_lock_status(priv, &lock_state);
- if (lock_state != 1) {
- msleep(5);
- watch_dog_count--;
- }
- }
- return lock_state;
-}
-
#define XC_TUNE_ANALOG 0
#define XC_TUNE_DIGITAL 1
static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
{
- int found = 0;
-
dprintk(1, "%s(%u)\n", __func__, freq_hz);
if (xc_set_rf_frequency(priv, freq_hz) != 0)
- return 0;
-
- if (mode == XC_TUNE_ANALOG) {
- if (wait_for_lock(priv) == 1)
- found = 1;
- }
+ return -EREMOTEIO;
- return found;
+ return 0;
}
static int xc_set_xtal(struct dvb_frontend *fe)
@@ -788,6 +766,7 @@ static int xc5000_set_digital_params(struct dvb_frontend *fe)
if (!bw)
bw = 6000000;
/* fall to OFDM handling */
+ /* fall through */
case SYS_DMBTH:
case SYS_DVBT:
case SYS_DVBT2:
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 7e0c9b795e52..34dc7e062471 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -105,6 +105,15 @@ static struct tda18271_config hauppauge_woodbury_tunerconfig = {
static void au0828_restart_dvb_streaming(struct work_struct *work);
+static void au0828_bulk_timeout(unsigned long data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) data;
+
+ dprintk(1, "%s called\n", __func__);
+ dev->bulk_timeout_running = 0;
+ schedule_work(&dev->restart_streaming);
+}
+
/*-------------------------------------------------------------------*/
static void urb_completion(struct urb *purb)
{
@@ -138,6 +147,13 @@ static void urb_completion(struct urb *purb)
ptr[0], purb->actual_length);
schedule_work(&dev->restart_streaming);
return;
+ } else if (dev->bulk_timeout_running == 1) {
+ /* The URB handler has fired, so cancel timer which would
+ * restart endpoint if we hadn't
+ */
+ dprintk(1, "%s cancelling bulk timeout\n", __func__);
+ dev->bulk_timeout_running = 0;
+ del_timer(&dev->bulk_timeout);
}
/* Feed the transport payload into the kernel demux */
@@ -160,6 +176,11 @@ static int stop_urb_transfer(struct au0828_dev *dev)
if (!dev->urb_streaming)
return 0;
+ if (dev->bulk_timeout_running == 1) {
+ dev->bulk_timeout_running = 0;
+ del_timer(&dev->bulk_timeout);
+ }
+
dev->urb_streaming = false;
for (i = 0; i < URB_COUNT; i++) {
if (dev->urbs[i]) {
@@ -232,6 +253,11 @@ static int start_urb_transfer(struct au0828_dev *dev)
}
dev->urb_streaming = true;
+
+ /* If we don't valid data within 1 second, restart stream */
+ mod_timer(&dev->bulk_timeout, jiffies + (HZ));
+ dev->bulk_timeout_running = 1;
+
return 0;
}
@@ -622,6 +648,10 @@ int au0828_dvb_register(struct au0828_dev *dev)
return ret;
}
+ dev->bulk_timeout.function = au0828_bulk_timeout;
+ dev->bulk_timeout.data = (unsigned long) dev;
+ init_timer(&dev->bulk_timeout);
+
return 0;
}
diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
index 9ec919c68482..9d82ec0a4b64 100644
--- a/drivers/media/usb/au0828/au0828-input.c
+++ b/drivers/media/usb/au0828/au0828-input.c
@@ -351,7 +351,7 @@ int au0828_rc_register(struct au0828_dev *dev)
if (err)
goto error;
- pr_info("Remote controller %s initalized\n", ir->name);
+ pr_info("Remote controller %s initialized\n", ir->name);
return 0;
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 88e59748ebc2..05e445fe0b77 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -195,6 +195,8 @@ struct au0828_dev {
/* Digital */
struct au0828_dvb dvb;
struct work_struct restart_streaming;
+ struct timer_list bulk_timeout;
+ int bulk_timeout_running;
#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog */
diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c
index b1d13444ff30..0efba0da0a45 100644
--- a/drivers/media/usb/cpia2/cpia2_core.c
+++ b/drivers/media/usb/cpia2/cpia2_core.c
@@ -173,7 +173,8 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_VP_DEVICEH;
break;
case CPIA2_CMD_SET_VP_BRIGHTNESS:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_BRIGHTNESS:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
@@ -183,14 +184,16 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
break;
case CPIA2_CMD_SET_CONTRAST:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_CONTRAST:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
cmd.start = CPIA2_VP_YRANGE;
break;
case CPIA2_CMD_SET_VP_SATURATION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_SATURATION:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
@@ -200,28 +203,32 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_VP5_MCUVSATURATION;
break;
case CPIA2_CMD_SET_VP_GPIO_DATA:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_GPIO_DATA:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
cmd.start = CPIA2_VP_GPIO_DATA;
break;
case CPIA2_CMD_SET_VP_GPIO_DIRECTION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
cmd.start = CPIA2_VP_GPIO_DIRECTION;
break;
case CPIA2_CMD_SET_VC_MP_GPIO_DATA:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.reg_count = 1;
cmd.start = CPIA2_VC_MP_DATA;
break;
case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /*fall through */
case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.reg_count = 1;
@@ -235,7 +242,8 @@ int cpia2_do_command(struct camera_data *cam,
cmd.buffer.block_data[0] = param;
break;
case CPIA2_CMD_SET_FLICKER_MODES:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_FLICKER_MODES:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
@@ -280,8 +288,9 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
break;
- case CPIA2_CMD_SET_USER_MODE: /* Then fall through */
+ case CPIA2_CMD_SET_USER_MODE:
cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_USER_MODE:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
@@ -300,14 +309,16 @@ int cpia2_do_command(struct camera_data *cam,
cmd.buffer.block_data[0] = param;
break;
case CPIA2_CMD_SET_WAKEUP:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_WAKEUP:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.reg_count = 1;
cmd.start = CPIA2_VC_WAKEUP;
break;
case CPIA2_CMD_SET_PW_CONTROL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_PW_CONTROL:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.reg_count = 1;
@@ -319,7 +330,8 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_VP_SYSTEMSTATE;
break;
case CPIA2_CMD_SET_SYSTEM_CTRL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_SYSTEM_CTRL:
cmd.req_mode =
CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
@@ -327,21 +339,24 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
break;
case CPIA2_CMD_SET_VP_SYSTEM_CTRL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
cmd.start = CPIA2_VP_SYSTEMCTRL;
break;
case CPIA2_CMD_SET_VP_EXP_MODES:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VP_EXP_MODES:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
cmd.start = CPIA2_VP_EXPOSURE_MODES;
break;
case CPIA2_CMD_SET_DEVICE_CONFIG:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_DEVICE_CONFIG:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
@@ -361,7 +376,8 @@ int cpia2_do_command(struct camera_data *cam,
cmd.start = CPIA2_SENSOR_CR1;
break;
case CPIA2_CMD_SET_VC_CONTROL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_VC_CONTROL:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
cmd.reg_count = 1;
@@ -395,7 +411,8 @@ int cpia2_do_command(struct camera_data *cam,
case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as
this register can also affect
flicker modes */
- cmd.buffer.block_data[0] = param; /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ /* fall through */
case CPIA2_CMD_GET_USER_EFFECTS:
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
cmd.reg_count = 1;
diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
index 58de80bff4c7..6276d9b2198b 100644
--- a/drivers/media/usb/cx231xx/Kconfig
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -52,6 +52,8 @@ config VIDEO_CX231XX_DVB
select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MN88473 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT
---help---
This adds support for DVB cards based on the
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index a1007d005290..e0daa9b6c2a0 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -868,6 +868,33 @@ struct cx231xx_board cx231xx_boards[] = {
.amux = CX231XX_AMUX_LINE_IN,
} },
},
+ [CX231XX_BOARD_ASTROMETA_T2HYBRID] = {
+ .name = "Astrometa T2hybrid",
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 1,
+ .output_mode = OUT_MODE_VIP11,
+ .agc_analog_digital_select_gpio = 0x01,
+ .ctl_pin_status_mask = 0xffffffc4,
+ .demod_addr = 0x18, /* 0x30 >> 1 */
+ .demod_i2c_master = I2C_1_MUX_1,
+ .gpio_pin_status_mask = 0xa,
+ .norm = V4L2_STD_NTSC,
+ .tuner_addr = 0x3a, /* 0x74 >> 1 */
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .tuner_sif_gpio = 0x05,
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_1_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ },
+ },
+ },
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
@@ -937,6 +964,8 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_TERRATEC_GRABBY},
{USB_DEVICE(0x1b80, 0xd3b2),
.driver_info = CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD},
+ {USB_DEVICE(0x15f4, 0x0135),
+ .driver_info = CX231XX_BOARD_ASTROMETA_T2HYBRID},
{},
};
@@ -1013,6 +1042,11 @@ void cx231xx_pre_card_setup(struct cx231xx *dev)
dev_info(dev->dev, "Identified as %s (card=%d)\n",
dev->board.name, dev->model);
+ if (CX231XX_BOARD_ASTROMETA_T2HYBRID == dev->model) {
+ /* turn on demodulator chip */
+ cx231xx_set_gpio_value(dev, 0x03, 0x01);
+ }
+
/* set the direction for GPIO pins */
if (dev->board.tuner_gpio) {
cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 46427fd3b220..ee3eeeb600f8 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -37,6 +37,8 @@
#include "mb86a20s.h"
#include "si2157.h"
#include "lgdt3306a.h"
+#include "r820t.h"
+#include "mn88473.h"
MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
@@ -164,6 +166,13 @@ static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = {
.xtalMHz = 25,
};
+static struct r820t_config astrometa_t2hybrid_r820t_config = {
+ .i2c_addr = 0x3a, /* 0x74 >> 1 */
+ .xtal = 16000000,
+ .rafael_chip = CHIP_R828D,
+ .max_i2c_msg_len = 2,
+};
+
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
@@ -1019,6 +1028,46 @@ static int dvb_init(struct cx231xx *dev)
dev->dvb->i2c_client_tuner = client;
break;
}
+ case CX231XX_BOARD_ASTROMETA_T2HYBRID:
+ {
+ struct i2c_client *client;
+ struct i2c_board_info info = {};
+ struct mn88473_config mn88473_config = {};
+
+ /* attach demodulator chip */
+ mn88473_config.i2c_wr_max = 16;
+ mn88473_config.xtal = 25000000;
+ mn88473_config.fe = &dev->dvb->frontend;
+
+ strlcpy(info.type, "mn88473", sizeof(info.type));
+ info.addr = dev->board.demod_addr;
+ info.platform_data = &mn88473_config;
+
+ request_module(info.type);
+ client = i2c_new_device(demod_i2c, &info);
+
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod = client;
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ /* attach tuner chip */
+ dvb_attach(r820t_attach, dev->dvb->frontend,
+ tuner_i2c,
+ &astrometa_t2hybrid_r820t_config);
+ break;
+ }
default:
dev_err(dev->dev,
"%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c
index 6e80f3c573f3..eecf074b0a48 100644
--- a/drivers/media/usb/cx231xx/cx231xx-input.c
+++ b/drivers/media/usb/cx231xx/cx231xx-input.c
@@ -30,7 +30,7 @@ static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol,
int rc;
u8 cmd, scancode;
- dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__);
+ dev_dbg(&ir->rc->dev, "%s\n", __func__);
/* poll IR chip */
rc = i2c_master_recv(ir->c, &cmd, 1);
@@ -48,8 +48,7 @@ static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol,
scancode = bitrev8(cmd);
- dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n",
- cmd, scancode);
+ dev_dbg(&ir->rc->dev, "cmd %02x, scan = %02x\n", cmd, scancode);
*protocol = RC_TYPE_OTHER;
*pscancode = scancode;
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index 6414188ffdfa..f67f86876625 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -1134,7 +1134,7 @@ void cx231xx_v4l2_create_entities(struct cx231xx *dev)
/* The DVB core will handle it */
if (dev->tuner_type == TUNER_ABSENT)
continue;
- /* fall though */
+ /* fall through */
default: /* just to shut up a gcc warning */
ent->function = MEDIA_ENT_F_CONN_RF;
break;
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index d9792ea4bbc6..986c64ba5b56 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -79,6 +79,7 @@
#define CX231XX_BOARD_HAUPPAUGE_955Q 21
#define CX231XX_BOARD_TERRATEC_GRABBY 22
#define CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD 23
+#define CX231XX_BOARD_ASTROMETA_T2HYBRID 24
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index caa1e6101f58..23bbbf367b51 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -36,7 +36,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
state->buf[0] = req->cmd;
state->buf[1] = state->seq++;
- state->buf[2] = req->i2c_addr;
+ state->buf[2] = req->i2c_addr << 1;
state->buf[3] = req->addr >> 8;
state->buf[4] = req->addr & 0xff;
state->buf[5] = req->mbox;
@@ -52,6 +52,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
case READ_I2C:
write = 0;
state->buf[2] |= 0x01; /* set I2C direction */
+ /* fall through */
case WRITE_I2C:
state->buf[0] = READ_WRITE_I2C;
break;
@@ -205,9 +206,9 @@ static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct af9015_state *state = d_to_priv(d);
- int ret = 0, i = 0;
+ int ret;
u16 addr;
- u8 uninitialized_var(mbox), addr_len;
+ u8 mbox, addr_len;
struct req_t req;
/*
@@ -232,84 +233,89 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
| addr 0x3a | | addr 0xc6 |
|____________| |____________|
*/
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
- while (i < num) {
- if (msg[i].addr == state->af9013_config[0].i2c_addr ||
- msg[i].addr == state->af9013_config[1].i2c_addr) {
- addr = msg[i].buf[0] << 8;
- addr += msg[i].buf[1];
- mbox = msg[i].buf[2];
- addr_len = 3;
- } else {
- addr = msg[i].buf[0];
- addr_len = 1;
- /* mbox is don't care in that case */
- }
+ if (msg[0].len == 0 || msg[0].flags & I2C_M_RD) {
+ addr = 0x0000;
+ mbox = 0;
+ addr_len = 0;
+ } else if (msg[0].len == 1) {
+ addr = msg[0].buf[0];
+ mbox = 0;
+ addr_len = 1;
+ } else if (msg[0].len == 2) {
+ addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0;
+ mbox = 0;
+ addr_len = 2;
+ } else {
+ addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0;
+ mbox = msg[0].buf[2];
+ addr_len = 3;
+ }
- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].len > 3 || msg[i+1].len > 61) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr == state->af9013_config[0].i2c_addr)
- req.cmd = READ_MEMORY;
- else
- req.cmd = READ_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i+1].len;
- req.data = &msg[i+1].buf[0];
- ret = af9015_ctrl_msg(d, &req);
- i += 2;
- } else if (msg[i].flags & I2C_M_RD) {
- if (msg[i].len > 61) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr == state->af9013_config[0].i2c_addr) {
- ret = -EINVAL;
- goto error;
- }
+ if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ /* i2c write */
+ if (msg[0].len > 21) {
+ ret = -EOPNOTSUPP;
+ goto err;
+ }
+ if (msg[0].addr == state->af9013_config[0].i2c_addr)
+ req.cmd = WRITE_MEMORY;
+ else
+ req.cmd = WRITE_I2C;
+ req.i2c_addr = msg[0].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[0].len-addr_len;
+ req.data = &msg[0].buf[addr_len];
+ ret = af9015_ctrl_msg(d, &req);
+ } else if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ /* i2c write + read */
+ if (msg[0].len > 3 || msg[1].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto err;
+ }
+ if (msg[0].addr == state->af9013_config[0].i2c_addr)
+ req.cmd = READ_MEMORY;
+ else
req.cmd = READ_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i].len;
- req.data = &msg[i].buf[0];
- ret = af9015_ctrl_msg(d, &req);
- i += 1;
- } else {
- if (msg[i].len > 21) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr == state->af9013_config[0].i2c_addr)
- req.cmd = WRITE_MEMORY;
- else
- req.cmd = WRITE_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i].len-addr_len;
- req.data = &msg[i].buf[addr_len];
- ret = af9015_ctrl_msg(d, &req);
- i += 1;
+ req.i2c_addr = msg[0].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ } else if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+ /* i2c read */
+ if (msg[0].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto err;
}
- if (ret)
- goto error;
-
+ if (msg[0].addr == state->af9013_config[0].i2c_addr) {
+ ret = -EINVAL;
+ goto err;
+ }
+ req.cmd = READ_I2C;
+ req.i2c_addr = msg[0].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[0].len;
+ req.data = &msg[0].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ } else {
+ ret = -EOPNOTSUPP;
+ dev_dbg(&d->udev->dev, "%s: unknown msg, num %u\n",
+ __func__, num);
}
- ret = i;
-
-error:
- mutex_unlock(&d->i2c_mutex);
+ if (ret)
+ goto err;
+ return num;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed %d\n", __func__, ret);
return ret;
}
@@ -471,6 +477,8 @@ static int af9015_read_config(struct dvb_usb_device *d)
if (d->udev->speed == USB_SPEED_FULL)
state->dual_mode = 0;
+ state->af9013_config[0].i2c_addr = AF9015_I2C_DEMOD;
+
if (state->dual_mode) {
/* read 2nd demodulator I2C address */
req.addr = AF9015_EEPROM_DEMOD2_I2C;
@@ -478,7 +486,7 @@ static int af9015_read_config(struct dvb_usb_device *d)
if (ret)
goto error;
- state->af9013_config[1].i2c_addr = val;
+ state->af9013_config[1].i2c_addr = val >> 1;
}
for (i = 0; i < state->dual_mode + 1; i++) {
@@ -733,9 +741,6 @@ static int af9015_copy_firmware(struct dvb_usb_device *d)
fw_params[2] = state->firmware_checksum >> 8;
fw_params[3] = state->firmware_checksum & 0xff;
- /* wait 2nd demodulator ready */
- msleep(100);
-
ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr,
0x98be, &val);
if (ret)
@@ -823,6 +828,9 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
/* copy firmware to 2nd demodulator */
if (state->dual_mode) {
+ /* Wait 2nd demodulator ready */
+ msleep(100);
+
ret = af9015_copy_firmware(adap_to_d(adap));
if (ret) {
dev_err(&adap_to_d(adap)->udev->dev,
@@ -870,12 +878,12 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
}
static struct mt2060_config af9015_mt2060_config = {
- .i2c_address = 0xc0,
+ .i2c_address = 0x60,
.clock_out = 0,
};
static struct qt1010_config af9015_qt1010_config = {
- .i2c_address = 0xc4,
+ .i2c_address = 0x62,
};
static struct tda18271_config af9015_tda18271_config = {
@@ -884,7 +892,7 @@ static struct tda18271_config af9015_tda18271_config = {
};
static struct mxl5005s_config af9015_mxl5003_config = {
- .i2c_address = 0xc6,
+ .i2c_address = 0x63,
.if_freq = IF_FREQ_4570000HZ,
.xtal_freq = CRYSTAL_FREQ_16000000HZ,
.agc_mode = MXL_SINGLE_AGC,
@@ -901,7 +909,7 @@ static struct mxl5005s_config af9015_mxl5003_config = {
};
static struct mxl5005s_config af9015_mxl5005_config = {
- .i2c_address = 0xc6,
+ .i2c_address = 0x63,
.if_freq = IF_FREQ_4570000HZ,
.xtal_freq = CRYSTAL_FREQ_16000000HZ,
.agc_mode = MXL_SINGLE_AGC,
@@ -918,12 +926,12 @@ static struct mxl5005s_config af9015_mxl5005_config = {
};
static struct mc44s803_config af9015_mc44s803_config = {
- .i2c_address = 0xc0,
+ .i2c_address = 0x60,
.dig_out = 1,
};
static struct tda18218_config af9015_tda18218_config = {
- .i2c_address = 0xc0,
+ .i2c_address = 0x60,
.i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */
};
@@ -954,7 +962,7 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
&af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
- ret = dvb_attach(tda18271_attach, adap->fe[0], 0xc0,
+ ret = dvb_attach(tda18271_attach, adap->fe[0], 0x60,
&adap_to_d(adap)->i2c_adap,
&af9015_tda18271_config) == NULL ? -ENODEV : 0;
break;
@@ -975,7 +983,7 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_ENV77H11D5:
- ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0xc0,
+ ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0x60,
&adap_to_d(adap)->i2c_adap,
DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
break;
@@ -987,7 +995,7 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
case AF9013_TUNER_MXL5007T:
ret = dvb_attach(mxl5007t_attach, adap->fe[0],
&adap_to_d(adap)->i2c_adap,
- 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
+ 0x60, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_UNKNOWN:
default:
@@ -1124,10 +1132,21 @@ static int af9015_init_endpoint(struct dvb_usb_device *d)
}
/* enable / disable mp2if2 */
- if (state->dual_mode)
+ if (state->dual_mode) {
ret = af9015_set_reg_bit(d, 0xd50b, 0);
- else
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xd520, 4);
+ if (ret)
+ goto error;
+ } else {
ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xd520, 4);
+ if (ret)
+ goto error;
+ }
error:
if (ret)
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h
index 2dd9231a8ece..3a9d9815ab7a 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.h
+++ b/drivers/media/usb/dvb-usb-v2/af9015.h
@@ -47,8 +47,8 @@
#define TS_USB20_MAX_PACKET_SIZE 512
#define TS_USB11_MAX_PACKET_SIZE 64
-#define AF9015_I2C_EEPROM 0xa0
-#define AF9015_I2C_DEMOD 0x38
+#define AF9015_I2C_EEPROM 0x50
+#define AF9015_I2C_DEMOD 0x1c
#define AF9015_USB_TIMEOUT 2000
/* EEPROM locations */
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 924adfdb660d..a91fdad8f8d4 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -207,15 +207,13 @@ static int lme2510_stream_restart(struct dvb_usb_device *d)
struct lme2510_state *st = d->priv;
u8 all_pids[] = LME_ALL_PIDS;
u8 stream_on[] = LME_ST_ON_W;
- int ret;
u8 rbuff[1];
if (st->pid_off)
- ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
- rbuff, sizeof(rbuff));
+ lme2510_usb_talk(d, all_pids, sizeof(all_pids),
+ rbuff, sizeof(rbuff));
/*Restart Stream Command*/
- ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
- rbuff, sizeof(rbuff));
- return ret;
+ return lme2510_usb_talk(d, stream_on, sizeof(stream_on),
+ rbuff, sizeof(rbuff));
}
static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out)
@@ -1065,6 +1063,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
}
break;
}
+ /* fall through */
case 0x22f0:
st->i2c_gate = 5;
adap->fe[0] = dvb_attach(m88rs2000_attach,
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
index ffb49c28b15a..0eb33e043079 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
@@ -316,7 +316,7 @@ fail:
static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
u8 index, u8 *wdata)
{
- int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ int ret = mxl111sf_ctrl_msg(state, wdata[0],
&wdata[1], 25, NULL, 0);
mxl_fail(ret);
@@ -326,7 +326,7 @@ static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
static int mxl111sf_i2c_get_data(struct mxl111sf_state *state,
u8 index, u8 *wdata, u8 *rdata)
{
- int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
+ int ret = mxl111sf_ctrl_msg(state, wdata[0],
&wdata[1], 25, rdata, 24);
mxl_fail(ret);
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index abf69d8fa469..b0d5904a4ea6 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -24,9 +24,6 @@
#include "lgdt3305.h"
#include "lg2160.h"
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE 64
-
int dvb_usb_mxl111sf_debug;
module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
@@ -55,27 +52,34 @@ MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+int mxl111sf_ctrl_msg(struct mxl111sf_state *state,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
+ struct dvb_usb_device *d = state->d;
int wo = (rbuf == NULL || rlen == 0); /* write-only */
int ret;
- u8 sndbuf[MAX_XFER_SIZE];
- if (1 + wlen > sizeof(sndbuf)) {
+ if (1 + wlen > MXL_MAX_XFER_SIZE) {
pr_warn("%s: len=%d is too big!\n", __func__, wlen);
return -EOPNOTSUPP;
}
pr_debug("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
- memset(sndbuf, 0, 1+wlen);
+ mutex_lock(&state->msg_lock);
+ memset(state->sndbuf, 0, 1+wlen);
+ memset(state->rcvbuf, 0, rlen);
+
+ state->sndbuf[0] = cmd;
+ memcpy(&state->sndbuf[1], wbuf, wlen);
- sndbuf[0] = cmd;
- memcpy(&sndbuf[1], wbuf, wlen);
+ ret = (wo) ? dvb_usbv2_generic_write(d, state->sndbuf, 1+wlen) :
+ dvb_usbv2_generic_rw(d, state->sndbuf, 1+wlen, state->rcvbuf,
+ rlen);
+
+ memcpy(rbuf, state->rcvbuf, rlen);
+ mutex_unlock(&state->msg_lock);
- ret = (wo) ? dvb_usbv2_generic_write(d, sndbuf, 1+wlen) :
- dvb_usbv2_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen);
mxl_fail(ret);
return ret;
@@ -91,7 +95,7 @@ int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
u8 buf[2];
int ret;
- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
+ ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_READ, &addr, 1, buf, 2);
if (mxl_fail(ret)) {
mxl_debug("error reading reg: 0x%02x", addr);
goto fail;
@@ -117,7 +121,7 @@ int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
pr_debug("W: (0x%02x, 0x%02x)\n", addr, data);
- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
+ ret = mxl111sf_ctrl_msg(state, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
if (mxl_fail(ret))
pr_err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
return ret;
@@ -926,6 +930,8 @@ static int mxl111sf_init(struct dvb_usb_device *d)
.len = sizeof(eeprom), .buf = eeprom },
};
+ mutex_init(&state->msg_lock);
+
ret = get_chip_info(state);
if (mxl_fail(ret))
pr_err("failed to get chip info during probe");
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
index 846260e0eec0..3e6f5880bd1e 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
@@ -19,6 +19,9 @@
#include <media/tveeprom.h>
#include <media/media-entity.h>
+/* Max transfer size done by I2C transfer functions */
+#define MXL_MAX_XFER_SIZE 64
+
#define MXL_EP1_REG_READ 1
#define MXL_EP2_REG_WRITE 2
#define MXL_EP3_INTERRUPT 3
@@ -86,6 +89,9 @@ struct mxl111sf_state {
struct mutex fe_lock;
u8 num_frontends;
struct mxl111sf_adap_state adap_state[3];
+ u8 sndbuf[MXL_MAX_XFER_SIZE];
+ u8 rcvbuf[MXL_MAX_XFER_SIZE];
+ struct mutex msg_lock;
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
struct media_entity tuner;
struct media_pad tuner_pads[2];
@@ -108,7 +114,7 @@ int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
/* needed for hardware i2c functions in mxl111sf-i2c.c:
* mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
-int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+int mxl111sf_ctrl_msg(struct mxl111sf_state *state,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
#define mxl_printk(kern, fmt, arg...) \
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 08acdd32e412..bea1b4764a66 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -215,13 +215,14 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
USB_CTRL_GET_TIMEOUT);
if (result < 0) {
deb_info("i2c read error (status = %d)\n", result);
- break;
+ goto unlock;
}
if (msg[i].len > sizeof(st->buf)) {
deb_info("buffer too small to fit %d bytes\n",
msg[i].len);
- return -EIO;
+ result = -EIO;
+ goto unlock;
}
memcpy(msg[i].buf, st->buf, msg[i].len);
@@ -233,8 +234,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
/* Write request */
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- mutex_unlock(&d->i2c_mutex);
- return -EINTR;
+ result = -EINTR;
+ goto unlock;
}
st->buf[0] = REQUEST_NEW_I2C_WRITE;
st->buf[1] = msg[i].addr << 1;
@@ -247,7 +248,9 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
if (msg[i].len > sizeof(st->buf) - 4) {
deb_info("i2c message to big: %d\n",
msg[i].len);
- return -EIO;
+ mutex_unlock(&d->usb_mutex);
+ result = -EIO;
+ goto unlock;
}
/* The Actual i2c payload */
@@ -269,8 +272,11 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
}
}
}
+ result = i;
+
+unlock:
mutex_unlock(&d->i2c_mutex);
- return i;
+ return result;
}
/*
@@ -281,7 +287,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct dib0700_state *st = d->priv;
- int i,len;
+ int i, len, result;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EINTR;
@@ -298,7 +304,8 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
if (msg[i].len > sizeof(st->buf) - 2) {
deb_info("i2c xfer to big: %d\n",
msg[i].len);
- return -EIO;
+ result = -EIO;
+ goto unlock;
}
memcpy(&st->buf[2], msg[i].buf, msg[i].len);
@@ -313,13 +320,15 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
if (len <= 0) {
deb_info("I2C read failed on address 0x%02x\n",
msg[i].addr);
- break;
+ result = -EIO;
+ goto unlock;
}
if (msg[i + 1].len > sizeof(st->buf)) {
deb_info("i2c xfer buffer to small for %d\n",
msg[i].len);
- return -EIO;
+ result = -EIO;
+ goto unlock;
}
memcpy(msg[i + 1].buf, st->buf, msg[i + 1].len);
@@ -328,14 +337,17 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
i++;
} else {
st->buf[0] = REQUEST_I2C_WRITE;
- if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0)
- break;
+ result = dib0700_ctrl_wr(d, st->buf, msg[i].len + 2);
+ if (result < 0)
+ goto unlock;
}
}
+ result = i;
+unlock:
mutex_unlock(&d->usb_mutex);
mutex_unlock(&d->i2c_mutex);
- return i;
+ return result;
}
static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 85ab3fa48f9a..6a57fc6d3472 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -1659,6 +1659,7 @@ static int dib8096_set_param_override(struct dvb_frontend *fe)
switch (band) {
default:
deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency);
+ /* fall through */
case BAND_VHF:
state->dib8000_ops.set_gpio(fe, 3, 0, 1);
break;
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
index 059ded59208e..f05f1fc80729 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
@@ -131,6 +131,11 @@ static void legacy_dvb_usb_read_remote_control(struct work_struct *work)
case REMOTE_KEY_PRESSED:
deb_rc("key pressed\n");
d->last_event = event;
+ input_event(d->input_dev, EV_KEY, event, 1);
+ input_sync(d->input_dev);
+ input_event(d->input_dev, EV_KEY, d->last_event, 0);
+ input_sync(d->input_dev);
+ break;
case REMOTE_KEY_REPEAT:
deb_rc("key repeated\n");
input_event(d->input_dev, EV_KEY, event, 1);
diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index 6e654e5026dd..57b187240110 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
@@ -1840,11 +1840,12 @@ static int dw2102_load_firmware(struct usb_device *dev,
switch (le16_to_cpu(dev->descriptor.idProduct)) {
case USB_PID_TEVII_S650:
dw2104_properties.rc.core.rc_codes = RC_MAP_TEVII_NEC;
+ /* fall through */
case USB_PID_DW2104:
reset = 1;
dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1,
DW210X_WRITE_MSG);
- /* break omitted intentionally */
+ /* fall through */
case USB_PID_DW3101:
reset = 0;
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
@@ -1877,6 +1878,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
break;
}
}
+ /* fall through */
case 0x2101:
dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2,
DW210X_READ_MSG);
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index a12b599a1fa2..4c57fd7929cb 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -1193,6 +1193,22 @@ struct em28xx_board em28xx_boards[] = {
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
+ [EM2884_BOARD_TERRATEC_H6] = {
+ .name = "Terratec Cinergy H6 rev. 2",
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+#if 0
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x41,
+ .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
+ .tuner_gpio = terratec_h5_gpio,
+#else
+ .tuner_type = TUNER_ABSENT,
+#endif
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
[EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
.name = "Hauppauge WinTV HVR 930C",
.has_dvb = 1,
@@ -2496,6 +2512,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2884_BOARD_TERRATEC_H5 },
{ USB_DEVICE(0x0ccd, 0x10b6), /* H5 Rev. 3 */
.driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10b2), /* H6 */
+ .driver_info = EM2884_BOARD_TERRATEC_H6 },
{ USB_DEVICE(0x0ccd, 0x0084),
.driver_info = EM2860_BOARD_TERRATEC_AV350 },
{ USB_DEVICE(0x0ccd, 0x0096),
@@ -2855,7 +2873,7 @@ static int em28xx_hint_board(struct em28xx *dev)
"Your board has no unique USB ID.\n"
"A hint were successfully done, based on eeprom hash.\n"
"This method is not 100%% failproof.\n"
- "If the board were missdetected, please email this log to:\n"
+ "If the board were misdetected, please email this log to:\n"
"\tV4L Mailing List <linux-media@vger.kernel.org>\n"
"Board detected as %s\n",
em28xx_boards[dev->model].name);
@@ -2885,7 +2903,7 @@ static int em28xx_hint_board(struct em28xx *dev)
"Your board has no unique USB ID.\n"
"A hint were successfully done, based on i2c devicelist hash.\n"
"This method is not 100%% failproof.\n"
- "If the board were missdetected, please email this log to:\n"
+ "If the board were misdetected, please email this log to:\n"
"\tV4L Mailing List <linux-media@vger.kernel.org>\n"
"Board detected as %s\n",
em28xx_boards[dev->model].name);
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 19ccff41c7eb..1d0d8cc06103 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -91,22 +91,16 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
if (len > URB_MAX_CTRL_SIZE)
return -EINVAL;
- em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
- pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8);
-
mutex_lock(&dev->ctrl_urb_lock);
ret = usb_control_msg(udev, pipe, req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, reg, dev->urb_buf, len, HZ);
if (ret < 0) {
- em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed\n",
+ em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed with error %i\n",
pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
req, 0, 0,
reg & 0xff, reg >> 8,
- len & 0xff, len >> 8);
+ len & 0xff, len >> 8, ret);
mutex_unlock(&dev->ctrl_urb_lock);
return usb_translate_errors(ret);
}
@@ -116,7 +110,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
mutex_unlock(&dev->ctrl_urb_lock);
- em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed <<< %*ph\n",
+ em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x <<< %*ph\n",
pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
req, 0, 0,
reg & 0xff, reg >> 8,
@@ -164,13 +158,6 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
return -EINVAL;
- em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph\n",
- pipe,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8, len, buf);
-
mutex_lock(&dev->ctrl_urb_lock);
memcpy(dev->urb_buf, buf, len);
ret = usb_control_msg(udev, pipe, req,
@@ -178,8 +165,22 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
0x0000, reg, dev->urb_buf, len, HZ);
mutex_unlock(&dev->ctrl_urb_lock);
- if (ret < 0)
+ if (ret < 0) {
+ em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph failed with error %i\n",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8, len, buf, ret);
return usb_translate_errors(ret);
+ }
+
+ em28xx_regdbg("(pipe 0x%08x): OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph\n",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8, len, buf);
if (dev->wait_after_write)
msleep(dev->wait_after_write);
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 82edd37f0d73..4a7db623fe29 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -1522,6 +1522,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2884_BOARD_ELGATO_EYETV_HYBRID_2008:
case EM2884_BOARD_CINERGY_HTC_STICK:
+ case EM2884_BOARD_TERRATEC_H6:
terratec_htc_stick_init(dev);
/* attach demodulator */
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 8c472d5adb50..60b195c157b8 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -982,8 +982,6 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
dev_err(&dev->intf->dev,
"%s: em28xx_i2_eeprom failed! retval [%d]\n",
__func__, retval);
-
- return retval;
}
}
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index eba75736e654..ca9673917ad5 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -821,7 +821,7 @@ static int em28xx_ir_init(struct em28xx *dev)
if (err)
goto error;
- dev_info(&dev->intf->dev, "Input extension successfully initalized\n");
+ dev_info(&dev->intf->dev, "Input extension successfully initialized\n");
return 0;
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index e8d97d5ec161..88084f24f033 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -148,6 +148,7 @@
#define EM28178_BOARD_PLEX_PX_BCUD 98
#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99
#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100
+#define EM2884_BOARD_TERRATEC_H6 101
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
index be5e25d1a2e8..6ad8d4849680 100644
--- a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
@@ -345,6 +345,11 @@ int s5k83a_start(struct sd *sd)
to assume that there is no better way of accomplishing this */
sd->rotation_thread = kthread_create(rotation_thread_function,
sd, "rotation thread");
+ if (IS_ERR(sd->rotation_thread)) {
+ err = PTR_ERR(sd->rotation_thread);
+ sd->rotation_thread = NULL;
+ return err;
+ }
wake_up_process(sd->rotation_thread);
/* Preinit the sensor */
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
index f4c41f043cda..cdb79c5f0c38 100644
--- a/drivers/media/usb/gspca/ov519.c
+++ b/drivers/media/usb/gspca/ov519.c
@@ -3526,7 +3526,8 @@ static void ov511_mode_init_regs(struct sd *sd)
sd->clockdiv = 0;
break;
}
- /* Fall through for 640x480 case */
+ /* For 640x480 case */
+ /* fall through */
default:
/* case 20: */
/* case 15: */
diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c
index 1dfc2de1fe77..f9ed9c950247 100644
--- a/drivers/media/usb/pulse8-cec/pulse8-cec.c
+++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c
@@ -51,7 +51,7 @@ MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
MODULE_LICENSE("GPL");
static int debug;
-static int persistent_config = 1;
+static int persistent_config;
module_param(debug, int, 0644);
module_param(persistent_config, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-1)");
@@ -148,18 +148,15 @@ static void pulse8_irq_work_handler(struct work_struct *work)
cec_received_msg(pulse8->adap, &pulse8->rx_msg);
break;
case MSGCODE_TRANSMIT_SUCCEEDED:
- cec_transmit_done(pulse8->adap, CEC_TX_STATUS_OK,
- 0, 0, 0, 0);
+ cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_OK);
break;
case MSGCODE_TRANSMIT_FAILED_ACK:
- cec_transmit_done(pulse8->adap, CEC_TX_STATUS_NACK,
- 0, 1, 0, 0);
+ cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_NACK);
break;
case MSGCODE_TRANSMIT_FAILED_LINE:
case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
- cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ERROR,
- 0, 0, 0, 1);
+ cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR);
break;
}
}
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
index f727b54a53c6..20a52b785fff 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
@@ -488,7 +488,7 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {
if (cnt > 8) cnt = 8;
printk(KERN_CONT " [");
- for (offs = 0; offs < (cnt>8?8:cnt); offs++) {
+ for (offs = 0; offs < cnt; offs++) {
if (offs) printk(KERN_CONT " ");
printk(KERN_CONT "%02x",msgs[idx].buf[offs]);
}
diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c
index 92f04db6bbae..043b2b97cee6 100644
--- a/drivers/media/usb/pwc/pwc-v4l.c
+++ b/drivers/media/usb/pwc/pwc-v4l.c
@@ -568,7 +568,8 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
pdev->gain_valid = true;
if (!DEVICE_USE_CODEC3(pdev->type))
break;
- /* Fall through for CODEC3 where autogain also controls expo */
+ /* For CODEC3 where autogain also controls expo */
+ /* fall through */
case V4L2_CID_EXPOSURE_AUTO:
if (pdev->exposure_valid && time_before(jiffies,
pdev->last_exposure_update + HZ / 4)) {
diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
index 4126552c9055..65692576690f 100644
--- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
+++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
@@ -98,16 +98,13 @@ static void rain_process_msg(struct rain *rain)
switch (stat) {
case 1:
- cec_transmit_done(rain->adap, CEC_TX_STATUS_OK,
- 0, 0, 0, 0);
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
break;
case 2:
- cec_transmit_done(rain->adap, CEC_TX_STATUS_NACK,
- 0, 1, 0, 0);
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
break;
default:
- cec_transmit_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE,
- 0, 0, 0, 1);
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
break;
}
}
@@ -119,20 +116,19 @@ static void rain_irq_work_handler(struct work_struct *work)
while (true) {
unsigned long flags;
- bool exit_loop = false;
char data;
spin_lock_irqsave(&rain->buf_lock, flags);
- exit_loop = rain->buf_len == 0;
- if (rain->buf_len) {
- data = rain->buf[rain->buf_rd_idx];
- rain->buf_len--;
- rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
+ if (!rain->buf_len) {
+ spin_unlock_irqrestore(&rain->buf_lock, flags);
+ break;
}
- spin_unlock_irqrestore(&rain->buf_lock, flags);
- if (exit_loop)
- break;
+ data = rain->buf[rain->buf_rd_idx];
+ rain->buf_len--;
+ rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
+
+ spin_unlock_irqrestore(&rain->buf_lock, flags);
if (!rain->cmd_started && data != '?')
continue;
@@ -296,7 +292,7 @@ static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
cec_msg_destination(msg), msg->msg[1]);
for (i = 2; i < msg->len; i++) {
snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
- strncat(cmd, hex, sizeof(cmd));
+ strlcat(cmd, hex, sizeof(cmd));
}
}
mutex_lock(&rain->write_lock);
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index a9d4484f7626..6a88b1dbb3a0 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -1803,6 +1803,8 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
default:
pr_info("s2255 unknown resp\n");
}
+ pdata++;
+ break;
default:
pdata++;
break;
diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c
index 985af9933c7e..c1d4505f84ea 100644
--- a/drivers/media/usb/stkwebcam/stk-sensor.c
+++ b/drivers/media/usb/stkwebcam/stk-sensor.c
@@ -41,6 +41,8 @@
/* It seems the i2c bus is controlled with these registers */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "stk-webcam.h"
#define STK_IIC_BASE (0x0200)
@@ -239,8 +241,8 @@ static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val)
} while (tmpval == 0 && i < MAX_RETRIES);
if (tmpval != STK_IIC_STAT_TX_OK) {
if (tmpval)
- STK_ERROR("stk_sensor_outb failed, status=0x%02x\n",
- tmpval);
+ pr_err("stk_sensor_outb failed, status=0x%02x\n",
+ tmpval);
return 1;
} else
return 0;
@@ -262,8 +264,8 @@ static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val)
} while (tmpval == 0 && i < MAX_RETRIES);
if (tmpval != STK_IIC_STAT_RX_OK) {
if (tmpval)
- STK_ERROR("stk_sensor_inb failed, status=0x%02x\n",
- tmpval);
+ pr_err("stk_sensor_inb failed, status=0x%02x\n",
+ tmpval);
return 1;
}
@@ -366,29 +368,29 @@ int stk_sensor_init(struct stk_camera *dev)
if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES)
|| stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS)
|| stk_sensor_outb(dev, REG_COM7, COM7_RESET)) {
- STK_ERROR("Sensor resetting failed\n");
+ pr_err("Sensor resetting failed\n");
return -ENODEV;
}
msleep(10);
/* Read the manufacturer ID: ov = 0x7FA2 */
if (stk_sensor_inb(dev, REG_MIDH, &idh)
|| stk_sensor_inb(dev, REG_MIDL, &idl)) {
- STK_ERROR("Strange error reading sensor ID\n");
+ pr_err("Strange error reading sensor ID\n");
return -ENODEV;
}
if (idh != 0x7f || idl != 0xa2) {
- STK_ERROR("Huh? you don't have a sensor from ovt\n");
+ pr_err("Huh? you don't have a sensor from ovt\n");
return -ENODEV;
}
if (stk_sensor_inb(dev, REG_PID, &idh)
|| stk_sensor_inb(dev, REG_VER, &idl)) {
- STK_ERROR("Could not read sensor model\n");
+ pr_err("Could not read sensor model\n");
return -ENODEV;
}
stk_sensor_write_regvals(dev, ov_initvals);
msleep(10);
- STK_INFO("OmniVision sensor detected, id %02X%02X at address %x\n",
- idh, idl, SENSOR_ADDRESS);
+ pr_info("OmniVision sensor detected, id %02X%02X at address %x\n",
+ idh, idl, SENSOR_ADDRESS);
return 0;
}
@@ -520,7 +522,8 @@ int stk_sensor_configure(struct stk_camera *dev)
case MODE_SXGA: com7 = COM7_FMT_SXGA;
dummylines = 0;
break;
- default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode);
+ default:
+ pr_err("Unsupported mode %d\n", dev->vsettings.mode);
return -EFAULT;
}
switch (dev->vsettings.palette) {
@@ -544,7 +547,8 @@ int stk_sensor_configure(struct stk_camera *dev)
com7 |= COM7_PBAYER;
rv = ov_fmt_bayer;
break;
- default: STK_ERROR("Unsupported colorspace\n");
+ default:
+ pr_err("Unsupported colorspace\n");
return -EFAULT;
}
/*FIXME sometimes the sensor go to a bad state
@@ -564,7 +568,7 @@ int stk_sensor_configure(struct stk_camera *dev)
switch (dev->vsettings.mode) {
case MODE_VGA:
if (stk_sensor_set_hw(dev, 302, 1582, 6, 486))
- STK_ERROR("stk_sensor_set_hw failed (VGA)\n");
+ pr_err("stk_sensor_set_hw failed (VGA)\n");
break;
case MODE_SXGA:
case MODE_CIF:
@@ -572,7 +576,7 @@ int stk_sensor_configure(struct stk_camera *dev)
case MODE_QCIF:
/*FIXME These settings seem ignored by the sensor
if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034))
- STK_ERROR("stk_sensor_set_hw failed (SXGA)\n");
+ pr_err("stk_sensor_set_hw failed (SXGA)\n");
*/
break;
}
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 6e7fc36b658f..90d4a08cda31 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -18,6 +18,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -175,15 +177,15 @@ static int stk_start_stream(struct stk_camera *dev)
if (!is_present(dev))
return -ENODEV;
if (!is_memallocd(dev) || !is_initialised(dev)) {
- STK_ERROR("FIXME: Buffers are not allocated\n");
+ pr_err("FIXME: Buffers are not allocated\n");
return -EFAULT;
}
ret = usb_set_interface(dev->udev, 0, 5);
if (ret < 0)
- STK_ERROR("usb_set_interface failed !\n");
+ pr_err("usb_set_interface failed !\n");
if (stk_sensor_wakeup(dev))
- STK_ERROR("error awaking the sensor\n");
+ pr_err("error awaking the sensor\n");
stk_camera_read_reg(dev, 0x0116, &value_116);
stk_camera_read_reg(dev, 0x0117, &value_117);
@@ -224,9 +226,9 @@ static int stk_stop_stream(struct stk_camera *dev)
unset_streaming(dev);
if (usb_set_interface(dev->udev, 0, 0))
- STK_ERROR("usb_set_interface failed !\n");
+ pr_err("usb_set_interface failed !\n");
if (stk_sensor_sleep(dev))
- STK_ERROR("error suspending the sensor\n");
+ pr_err("error suspending the sensor\n");
}
return 0;
}
@@ -313,7 +315,7 @@ static void stk_isoc_handler(struct urb *urb)
dev = (struct stk_camera *) urb->context;
if (dev == NULL) {
- STK_ERROR("isoc_handler called with NULL device !\n");
+ pr_err("isoc_handler called with NULL device !\n");
return;
}
@@ -326,14 +328,13 @@ static void stk_isoc_handler(struct urb *urb)
spin_lock_irqsave(&dev->spinlock, flags);
if (urb->status != -EINPROGRESS && urb->status != 0) {
- STK_ERROR("isoc_handler: urb->status == %d\n", urb->status);
+ pr_err("isoc_handler: urb->status == %d\n", urb->status);
goto resubmit;
}
if (list_empty(&dev->sio_avail)) {
/*FIXME Stop streaming after a while */
- (void) (printk_ratelimit() &&
- STK_ERROR("isoc_handler without available buffer!\n"));
+ pr_err_ratelimited("isoc_handler without available buffer!\n");
goto resubmit;
}
fb = list_first_entry(&dev->sio_avail,
@@ -343,8 +344,8 @@ static void stk_isoc_handler(struct urb *urb)
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status != 0) {
if (urb->iso_frame_desc[i].status != -EXDEV)
- STK_ERROR("Frame %d has error %d\n", i,
- urb->iso_frame_desc[i].status);
+ pr_err("Frame %d has error %d\n",
+ i, urb->iso_frame_desc[i].status);
continue;
}
framelen = urb->iso_frame_desc[i].actual_length;
@@ -368,9 +369,8 @@ static void stk_isoc_handler(struct urb *urb)
/* This marks a new frame */
if (fb->v4lbuf.bytesused != 0
&& fb->v4lbuf.bytesused != dev->frame_size) {
- (void) (printk_ratelimit() &&
- STK_ERROR("frame %d, bytesused=%d, skipping\n",
- i, fb->v4lbuf.bytesused));
+ pr_err_ratelimited("frame %d, bytesused=%d, skipping\n",
+ i, fb->v4lbuf.bytesused);
fb->v4lbuf.bytesused = 0;
fill = fb->buffer;
} else if (fb->v4lbuf.bytesused == dev->frame_size) {
@@ -395,8 +395,7 @@ static void stk_isoc_handler(struct urb *urb)
/* Our buffer is full !!! */
if (framelen + fb->v4lbuf.bytesused > dev->frame_size) {
- (void) (printk_ratelimit() &&
- STK_ERROR("Frame buffer overflow, lost sync\n"));
+ pr_err_ratelimited("Frame buffer overflow, lost sync\n");
/*FIXME Do something here? */
continue;
}
@@ -414,8 +413,8 @@ resubmit:
urb->dev = dev->udev;
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret != 0) {
- STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n",
- ret);
+ pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n",
+ ret);
}
}
@@ -433,32 +432,31 @@ static int stk_prepare_iso(struct stk_camera *dev)
udev = dev->udev;
if (dev->isobufs)
- STK_ERROR("isobufs already allocated. Bad\n");
+ pr_err("isobufs already allocated. Bad\n");
else
dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs),
GFP_KERNEL);
if (dev->isobufs == NULL) {
- STK_ERROR("Unable to allocate iso buffers\n");
+ pr_err("Unable to allocate iso buffers\n");
return -ENOMEM;
}
for (i = 0; i < MAX_ISO_BUFS; i++) {
if (dev->isobufs[i].data == NULL) {
kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
if (kbuf == NULL) {
- STK_ERROR("Failed to allocate iso buffer %d\n",
- i);
+ pr_err("Failed to allocate iso buffer %d\n", i);
goto isobufs_out;
}
dev->isobufs[i].data = kbuf;
} else
- STK_ERROR("isobuf data already allocated\n");
+ pr_err("isobuf data already allocated\n");
if (dev->isobufs[i].urb == NULL) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL)
goto isobufs_out;
dev->isobufs[i].urb = urb;
} else {
- STK_ERROR("Killing URB\n");
+ pr_err("Killing URB\n");
usb_kill_urb(dev->isobufs[i].urb);
urb = dev->isobufs[i].urb;
}
@@ -567,7 +565,7 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
{
int i;
if (dev->sio_bufs != NULL)
- STK_ERROR("sio_bufs already allocated\n");
+ pr_err("sio_bufs already allocated\n");
else {
dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer),
GFP_KERNEL);
@@ -690,7 +688,7 @@ static ssize_t stk_read(struct file *fp, char __user *buf,
spin_lock_irqsave(&dev->spinlock, flags);
if (list_empty(&dev->sio_full)) {
spin_unlock_irqrestore(&dev->spinlock, flags);
- STK_ERROR("BUG: No siobufs ready\n");
+ pr_err("BUG: No siobufs ready\n");
return 0;
}
sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list);
@@ -907,7 +905,7 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
stk_sizes[i].m != dev->vsettings.mode; i++)
;
if (i == ARRAY_SIZE(stk_sizes)) {
- STK_ERROR("ERROR: mode invalid\n");
+ pr_err("ERROR: mode invalid\n");
return -EINVAL;
}
pix_format->width = stk_sizes[i].w;
@@ -985,7 +983,7 @@ static int stk_setup_format(struct stk_camera *dev)
stk_sizes[i].m != dev->vsettings.mode)
i++;
if (i == ARRAY_SIZE(stk_sizes)) {
- STK_ERROR("Something is broken in %s\n", __func__);
+ pr_err("Something is broken in %s\n", __func__);
return -EFAULT;
}
/* This registers controls some timings, not sure of what. */
@@ -1241,7 +1239,7 @@ static void stk_v4l_dev_release(struct video_device *vd)
struct stk_camera *dev = vdev_to_camera(vd);
if (dev->sio_bufs != NULL || dev->isobufs != NULL)
- STK_ERROR("We are leaking memory\n");
+ pr_err("We are leaking memory\n");
usb_put_intf(dev->interface);
kfree(dev);
}
@@ -1264,10 +1262,10 @@ static int stk_register_video_device(struct stk_camera *dev)
video_set_drvdata(&dev->vdev, dev);
err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
if (err)
- STK_ERROR("v4l registration failed\n");
+ pr_err("v4l registration failed\n");
else
- STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n",
- video_device_node_name(&dev->vdev));
+ pr_info("Syntek USB2.0 Camera is now controlling device %s\n",
+ video_device_node_name(&dev->vdev));
return err;
}
@@ -1288,7 +1286,7 @@ static int stk_camera_probe(struct usb_interface *interface,
dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL);
if (dev == NULL) {
- STK_ERROR("Out of memory !\n");
+ pr_err("Out of memory !\n");
return -ENOMEM;
}
err = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
@@ -1352,7 +1350,7 @@ static int stk_camera_probe(struct usb_interface *interface,
}
}
if (!dev->isoc_ep) {
- STK_ERROR("Could not find isoc-in endpoint");
+ pr_err("Could not find isoc-in endpoint\n");
err = -ENODEV;
goto error;
}
@@ -1387,8 +1385,8 @@ static void stk_camera_disconnect(struct usb_interface *interface)
wake_up_interruptible(&dev->wait_frame);
- STK_INFO("Syntek USB2.0 Camera release resources device %s\n",
- video_device_node_name(&dev->vdev));
+ pr_info("Syntek USB2.0 Camera release resources device %s\n",
+ video_device_node_name(&dev->vdev));
video_unregister_device(&dev->vdev);
v4l2_ctrl_handler_free(&dev->hdl);
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h
index 0284120ce246..5cecbdc97573 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.h
+++ b/drivers/media/usb/stkwebcam/stk-webcam.h
@@ -31,12 +31,6 @@
#define ISO_MAX_FRAME_SIZE 3 * 1024
#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
-
-#define PREFIX "stkwebcam: "
-#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args)
-#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args)
-#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args)
-
struct stk_iso_buf {
void *data;
int length;
diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c
index 39c15bb2b20c..1a033f57fcc1 100644
--- a/drivers/media/usb/tm6000/tm6000-input.c
+++ b/drivers/media/usb/tm6000/tm6000-input.c
@@ -63,7 +63,6 @@ struct tm6000_IR {
u8 wait:1;
u8 pwled:2;
u8 submit_urb:1;
- u16 key_addr;
struct urb *int_urb;
/* IR device properties */
@@ -321,9 +320,6 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
dprintk(2, "%s\n",__func__);
- if ((rc->rc_map.scan) && (*rc_type == RC_BIT_NEC))
- ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
-
ir->rc_type = *rc_type;
tm6000_ir_config(ir);
diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c
index 5a3f788ad033..fdf6b6e285da 100644
--- a/drivers/media/usb/usbvision/usbvision-i2c.c
+++ b/drivers/media/usb/usbvision/usbvision-i2c.c
@@ -311,10 +311,13 @@ usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
switch (len) {
case 4:
buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4);
+ /* fall through */
case 3:
buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3);
+ /* fall through */
case 2:
buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2);
+ /* fall through */
case 1:
buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1);
break;
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index f9c3325aa4d4..756322c4ac05 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1427,8 +1427,8 @@ static int usbvision_probe(struct usb_interface *intf,
int model, i, ret;
PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
- dev->descriptor.idVendor,
- dev->descriptor.idProduct, ifnum);
+ le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct), ifnum);
model = devid->driver_info;
if (model < 0 || model >= usbvision_device_data_size) {
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 46d6be0bb316..70842c5af05b 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2013,6 +2013,7 @@ static int uvc_probe(struct usb_interface *intf,
{
struct usb_device *udev = interface_to_usbdev(intf);
struct uvc_device *dev;
+ int function;
int ret;
if (id->idVendor && id->idProduct)
@@ -2044,9 +2045,27 @@ static int uvc_probe(struct usb_interface *intf,
strlcpy(dev->name, udev->product, sizeof dev->name);
else
snprintf(dev->name, sizeof dev->name,
- "UVC Camera (%04x:%04x)",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
+ "UVC Camera (%04x:%04x)",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /*
+ * Add iFunction or iInterface to names when available as additional
+ * distinguishers between interfaces. iFunction is prioritized over
+ * iInterface which matches Windows behavior at the point of writing.
+ */
+ if (intf->intf_assoc && intf->intf_assoc->iFunction != 0)
+ function = intf->intf_assoc->iFunction;
+ else
+ function = intf->cur_altsetting->desc.iInterface;
+ if (function != 0) {
+ size_t len;
+
+ strlcat(dev->name, ": ", sizeof(dev->name));
+ len = strlen(dev->name);
+ usb_string(udev, function, dev->name + len,
+ sizeof(dev->name) - len);
+ }
/* Parse the Video Class control descriptor. */
if (uvc_parse_control(dev) < 0) {
@@ -2441,6 +2460,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_BUILTIN_ISIGHT },
+ /* Apple Built-In iSight via iBridge */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05ac,
+ .idProduct = 0x8600,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
/* Foxlink ("HP Webcam" on HP Mini 5103) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 47d93a938dde..fb86d6af398d 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1323,11 +1323,11 @@ static void uvc_video_complete(struct urb *urb)
default:
uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
"completion handler.\n", urb->status);
-
+ /* fall through */
case -ENOENT: /* usb_kill_urb() called. */
if (stream->frozen)
return;
-
+ /* fall through */
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 6b1b78ff1417..a35c33686abf 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -55,6 +55,9 @@ config V4L2_FLASH_LED_CLASS
When in doubt, say N.
+config V4L2_FWNODE
+ tristate
+
# Used by drivers that need Videobuf modules
config VIDEOBUF_GEN
tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 795a5352761d..098ad5fd5231 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -10,9 +10,7 @@ videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
ifeq ($(CONFIG_COMPAT),y)
videodev-objs += v4l2-compat-ioctl32.o
endif
-ifeq ($(CONFIG_OF),y)
- videodev-objs += v4l2-of.o
-endif
+obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
ifeq ($(CONFIG_TRACEPOINTS),y)
videodev-objs += vb2-trace.o v4l2-trace.o
endif
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index e48b7c032c95..8db45dfc271b 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -43,8 +43,6 @@
#define UNSET (-1U)
-#define PREFIX (t->i2c->dev.driver->name)
-
/*
* Driver modprobe parameters
*/
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 96cc733f35ef..851f128eba22 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -12,8 +12,10 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/list.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -40,10 +42,14 @@ static bool match_devname(struct v4l2_subdev *sd,
return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
}
-static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
- return !of_node_cmp(of_node_full_name(sd->of_node),
- of_node_full_name(asd->match.of.node));
+ if (!is_of_node(sd->fwnode) || !is_of_node(asd->match.fwnode.fwnode))
+ return sd->fwnode == asd->match.fwnode.fwnode;
+
+ return !of_node_cmp(of_node_full_name(to_of_node(sd->fwnode)),
+ of_node_full_name(
+ to_of_node(asd->match.fwnode.fwnode)));
}
static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
@@ -77,8 +83,8 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *
case V4L2_ASYNC_MATCH_I2C:
match = match_i2c;
break;
- case V4L2_ASYNC_MATCH_OF:
- match = match_of;
+ case V4L2_ASYNC_MATCH_FWNODE:
+ match = match_fwnode;
break;
default:
/* Cannot happen, unless someone breaks us */
@@ -143,7 +149,8 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_subdev *asd;
int i;
- if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
+ if (!v4l2_dev || !notifier->num_subdevs ||
+ notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
@@ -157,7 +164,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
case V4L2_ASYNC_MATCH_CUSTOM:
case V4L2_ASYNC_MATCH_DEVNAME:
case V4L2_ASYNC_MATCH_I2C:
- case V4L2_ASYNC_MATCH_OF:
+ case V4L2_ASYNC_MATCH_FWNODE:
break;
default:
dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
@@ -204,7 +211,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
if (!notifier->v4l2_dev)
return;
- dev = kmalloc_array(n_subdev, sizeof(*dev), GFP_KERNEL);
+ dev = kvmalloc_array(n_subdev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_err(notifier->v4l2_dev->dev,
"Failed to allocate device cache!\n");
@@ -260,7 +267,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
}
put_device(d);
}
- kfree(dev);
+ kvfree(dev);
notifier->v4l2_dev = NULL;
@@ -280,8 +287,8 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
* (struct v4l2_subdev.dev), and async sub-device does not
* exist independently of the device at any point of time.
*/
- if (!sd->of_node && sd->dev)
- sd->of_node = sd->dev->of_node;
+ if (!sd->fwnode && sd->dev)
+ sd->fwnode = dev_fwnode(sd->dev);
mutex_lock(&list_lock);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index ec42872d11cf..dd1db678718c 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -19,6 +19,7 @@
*/
#include <linux/ctype.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <media/v4l2-ioctl.h>
@@ -886,6 +887,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_PIXEL_RATE: return "Pixel Rate";
case V4L2_CID_TEST_PATTERN: return "Test Pattern";
case V4L2_CID_DEINTERLACING_MODE: return "Deinterlacing Mode";
+ case V4L2_CID_DIGITAL_GAIN: return "Digital Gain";
/* DV controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1739,14 +1741,15 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
unsigned nr_of_controls_hint,
struct lock_class_key *key, const char *name)
{
+ mutex_init(&hdl->_lock);
hdl->lock = &hdl->_lock;
- mutex_init(hdl->lock);
lockdep_set_class_and_name(hdl->lock, key, name);
INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs);
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
- hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
- GFP_KERNEL);
+ hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
+ sizeof(hdl->buckets[0]),
+ GFP_KERNEL | __GFP_ZERO);
hdl->error = hdl->buckets ? 0 : -ENOMEM;
return hdl->error;
}
@@ -1773,13 +1776,14 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
list_del(&ctrl->node);
list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
list_del(&sev->node);
- kfree(ctrl);
+ kvfree(ctrl);
}
- kfree(hdl->buckets);
+ kvfree(hdl->buckets);
hdl->buckets = NULL;
hdl->cached = NULL;
hdl->error = 0;
mutex_unlock(hdl->lock);
+ mutex_destroy(&hdl->_lock);
}
EXPORT_SYMBOL(v4l2_ctrl_handler_free);
@@ -2022,7 +2026,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
is_array)
sz_extra += 2 * tot_ctrl_size;
- ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
if (ctrl == NULL) {
handler_set_err(hdl, -ENOMEM);
return NULL;
@@ -2071,7 +2075,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
}
if (handler_new_ref(hdl, ctrl)) {
- kfree(ctrl);
+ kvfree(ctrl);
return NULL;
}
mutex_lock(hdl->lock);
@@ -2444,14 +2448,16 @@ int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd)
EXPORT_SYMBOL(v4l2_ctrl_subdev_log_status);
/* Call s_ctrl for all controls owned by the handler */
-int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+int __v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
{
struct v4l2_ctrl *ctrl;
int ret = 0;
if (hdl == NULL)
return 0;
- mutex_lock(hdl->lock);
+
+ lockdep_assert_held(hdl->lock);
+
list_for_each_entry(ctrl, &hdl->ctrls, node)
ctrl->done = false;
@@ -2476,7 +2482,22 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
if (ret)
break;
}
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__v4l2_ctrl_handler_setup);
+
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ int ret;
+
+ if (hdl == NULL)
+ return 0;
+
+ mutex_lock(hdl->lock);
+ ret = __v4l2_ctrl_handler_setup(hdl);
mutex_unlock(hdl->lock);
+
return ret;
}
EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
@@ -2824,8 +2845,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc_array(cs->count, sizeof(helper[0]),
- GFP_KERNEL);
+ helpers = kvmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
if (helpers == NULL)
return -ENOMEM;
}
@@ -2877,7 +2898,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
}
if (cs->count > ARRAY_SIZE(helper))
- kfree(helpers);
+ kvfree(helpers);
return ret;
}
EXPORT_SYMBOL(v4l2_g_ext_ctrls);
@@ -3079,8 +3100,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc_array(cs->count, sizeof(helper[0]),
- GFP_KERNEL);
+ helpers = kvmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
if (!helpers)
return -ENOMEM;
}
@@ -3157,7 +3178,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
}
if (cs->count > ARRAY_SIZE(helper))
- kfree(helpers);
+ kvfree(helpers);
return ret;
}
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
index a75df6cb141f..968c2eb08b5a 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -21,6 +21,7 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
+#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/export.h>
@@ -214,7 +215,8 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
if (elems < 1)
elems = 1;
- sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
+ sev = kvzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems,
+ GFP_KERNEL);
if (!sev)
return -ENOMEM;
for (i = 0; i < elems; i++)
@@ -232,7 +234,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
if (found_ev) {
- kfree(sev);
+ kvfree(sev);
return 0; /* Already listening */
}
@@ -304,7 +306,7 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
if (sev && sev->ops && sev->ops->del)
sev->ops->del(sev);
- kfree(sev);
+ kvfree(sev);
return 0;
}
diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
index 794e563f24f8..7b8288108e8a 100644
--- a/drivers/media/v4l2-core/v4l2-flash-led-class.c
+++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
@@ -12,7 +12,7 @@
#include <linux/led-class-flash.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <media/v4l2-flash-led-class.h>
@@ -612,7 +612,7 @@ static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
static const struct v4l2_subdev_ops v4l2_flash_subdev_ops;
struct v4l2_flash *v4l2_flash_init(
- struct device *dev, struct device_node *of_node,
+ struct device *dev, struct fwnode_handle *fwn,
struct led_classdev_flash *fled_cdev,
struct led_classdev_flash *iled_cdev,
const struct v4l2_flash_ops *ops,
@@ -638,7 +638,7 @@ struct v4l2_flash *v4l2_flash_init(
v4l2_flash->iled_cdev = iled_cdev;
v4l2_flash->ops = ops;
sd->dev = dev;
- sd->of_node = of_node ? of_node : led_cdev->dev->of_node;
+ sd->fwnode = fwn ? fwn : dev_fwnode(led_cdev->dev);
v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
sd->internal_ops = &v4l2_flash_subdev_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -654,7 +654,7 @@ struct v4l2_flash *v4l2_flash_init(
if (ret < 0)
goto err_init_controls;
- of_node_get(sd->of_node);
+ fwnode_handle_get(sd->fwnode);
ret = v4l2_async_register_subdev(sd);
if (ret < 0)
@@ -663,7 +663,7 @@ struct v4l2_flash *v4l2_flash_init(
return v4l2_flash;
err_async_register_sd:
- of_node_put(sd->of_node);
+ fwnode_handle_put(sd->fwnode);
v4l2_ctrl_handler_free(sd->ctrl_handler);
err_init_controls:
media_entity_cleanup(&sd->entity);
@@ -683,7 +683,7 @@ void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
v4l2_async_unregister_subdev(sd);
- of_node_put(sd->of_node);
+ fwnode_handle_put(sd->fwnode);
v4l2_ctrl_handler_free(sd->ctrl_handler);
media_entity_cleanup(&sd->entity);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
new file mode 100644
index 000000000000..153c53ca3925
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -0,0 +1,345 @@
+/*
+ * V4L2 fwnode binding parsing library
+ *
+ * The origins of the V4L2 fwnode library are in V4L2 OF library that
+ * formerly was located in v4l2-of.c.
+ *
+ * Copyright (c) 2016 Intel Corporation.
+ * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
+ *
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Copyright (C) 2012 Renesas Electronics Corp.
+ * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <media/v4l2-fwnode.h>
+
+static int v4l2_fwnode_endpoint_parse_csi_bus(struct fwnode_handle *fwnode,
+ struct v4l2_fwnode_endpoint *vep)
+{
+ struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
+ bool have_clk_lane = false;
+ unsigned int flags = 0, lanes_used = 0;
+ unsigned int i;
+ u32 v;
+ int rval;
+
+ rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
+ if (rval > 0) {
+ u32 array[ARRAY_SIZE(bus->data_lanes)];
+
+ bus->num_data_lanes =
+ min_t(int, ARRAY_SIZE(bus->data_lanes), rval);
+
+ fwnode_property_read_u32_array(fwnode, "data-lanes", array,
+ bus->num_data_lanes);
+
+ for (i = 0; i < bus->num_data_lanes; i++) {
+ if (lanes_used & BIT(array[i]))
+ pr_warn("duplicated lane %u in data-lanes\n",
+ array[i]);
+ lanes_used |= BIT(array[i]);
+
+ bus->data_lanes[i] = array[i];
+ }
+ }
+
+ rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL,
+ 0);
+ if (rval > 0) {
+ u32 array[ARRAY_SIZE(bus->lane_polarities)];
+
+ if (rval < 1 + bus->num_data_lanes /* clock + data */) {
+ pr_warn("too few lane-polarities entries (need %u, got %u)\n",
+ 1 + bus->num_data_lanes, rval);
+ return -EINVAL;
+ }
+
+ fwnode_property_read_u32_array(fwnode, "lane-polarities", array,
+ 1 + bus->num_data_lanes);
+
+ for (i = 0; i < 1 + bus->num_data_lanes; i++)
+ bus->lane_polarities[i] = array[i];
+ }
+
+ if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
+ if (lanes_used & BIT(v))
+ pr_warn("duplicated lane %u in clock-lanes\n", v);
+ lanes_used |= BIT(v);
+
+ bus->clock_lane = v;
+ have_clk_lane = true;
+ }
+
+ if (fwnode_property_present(fwnode, "clock-noncontinuous"))
+ flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
+ else if (have_clk_lane || bus->num_data_lanes > 0)
+ flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ bus->flags = flags;
+ vep->bus_type = V4L2_MBUS_CSI2;
+
+ return 0;
+}
+
+static void v4l2_fwnode_endpoint_parse_parallel_bus(
+ struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep)
+{
+ struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
+ unsigned int flags = 0;
+ u32 v;
+
+ if (!fwnode_property_read_u32(fwnode, "hsync-active", &v))
+ flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
+ V4L2_MBUS_HSYNC_ACTIVE_LOW;
+
+ if (!fwnode_property_read_u32(fwnode, "vsync-active", &v))
+ flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
+ V4L2_MBUS_VSYNC_ACTIVE_LOW;
+
+ if (!fwnode_property_read_u32(fwnode, "field-even-active", &v))
+ flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
+ V4L2_MBUS_FIELD_EVEN_LOW;
+ if (flags)
+ vep->bus_type = V4L2_MBUS_PARALLEL;
+ else
+ vep->bus_type = V4L2_MBUS_BT656;
+
+ if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v))
+ flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
+ V4L2_MBUS_PCLK_SAMPLE_FALLING;
+
+ if (!fwnode_property_read_u32(fwnode, "data-active", &v))
+ flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
+ V4L2_MBUS_DATA_ACTIVE_LOW;
+
+ if (fwnode_property_present(fwnode, "slave-mode"))
+ flags |= V4L2_MBUS_SLAVE;
+ else
+ flags |= V4L2_MBUS_MASTER;
+
+ if (!fwnode_property_read_u32(fwnode, "bus-width", &v))
+ bus->bus_width = v;
+
+ if (!fwnode_property_read_u32(fwnode, "data-shift", &v))
+ bus->data_shift = v;
+
+ if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v))
+ flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
+ V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
+
+ bus->flags = flags;
+
+}
+
+/**
+ * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties
+ * @fwnode: pointer to the endpoint's fwnode handle
+ * @vep: pointer to the V4L2 fwnode data structure
+ *
+ * All properties are optional. If none are found, we don't set any flags. This
+ * means the port has a static configuration and no properties have to be
+ * specified explicitly. If any properties that identify the bus as parallel
+ * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if
+ * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we
+ * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a
+ * reference to @fwnode.
+ *
+ * NOTE: This function does not parse properties the size of which is variable
+ * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in
+ * new drivers instead.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode,
+ struct v4l2_fwnode_endpoint *vep)
+{
+ int rval;
+
+ fwnode_graph_parse_endpoint(fwnode, &vep->base);
+
+ /* Zero fields from bus_type to until the end */
+ memset(&vep->bus_type, 0, sizeof(*vep) -
+ offsetof(typeof(*vep), bus_type));
+
+ rval = v4l2_fwnode_endpoint_parse_csi_bus(fwnode, vep);
+ if (rval)
+ return rval;
+ /*
+ * Parse the parallel video bus properties only if none
+ * of the MIPI CSI-2 specific properties were found.
+ */
+ if (vep->bus.mipi_csi2.flags == 0)
+ v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse);
+
+/*
+ * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by
+ * v4l2_fwnode_endpoint_alloc_parse()
+ * @vep - the V4L2 fwnode the resources of which are to be released
+ *
+ * It is safe to call this function with NULL argument or on a V4L2 fwnode the
+ * parsing of which failed.
+ */
+void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep)
+{
+ if (IS_ERR_OR_NULL(vep))
+ return;
+
+ kfree(vep->link_frequencies);
+ kfree(vep);
+}
+EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free);
+
+/**
+ * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties
+ * @fwnode: pointer to the endpoint's fwnode handle
+ *
+ * All properties are optional. If none are found, we don't set any flags. This
+ * means the port has a static configuration and no properties have to be
+ * specified explicitly. If any properties that identify the bus as parallel
+ * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if
+ * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we
+ * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a
+ * reference to @fwnode.
+ *
+ * v4l2_fwnode_endpoint_alloc_parse() has two important differences to
+ * v4l2_fwnode_endpoint_parse():
+ *
+ * 1. It also parses variable size data.
+ *
+ * 2. The memory it has allocated to store the variable size data must be freed
+ * using v4l2_fwnode_endpoint_free() when no longer needed.
+ *
+ * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer
+ * on error.
+ */
+struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse(
+ struct fwnode_handle *fwnode)
+{
+ struct v4l2_fwnode_endpoint *vep;
+ int rval;
+
+ vep = kzalloc(sizeof(*vep), GFP_KERNEL);
+ if (!vep)
+ return ERR_PTR(-ENOMEM);
+
+ rval = v4l2_fwnode_endpoint_parse(fwnode, vep);
+ if (rval < 0)
+ goto out_err;
+
+ rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
+ NULL, 0);
+ if (rval < 0)
+ goto out_err;
+
+ vep->link_frequencies =
+ kmalloc_array(rval, sizeof(*vep->link_frequencies), GFP_KERNEL);
+ if (!vep->link_frequencies) {
+ rval = -ENOMEM;
+ goto out_err;
+ }
+
+ vep->nr_of_link_frequencies = rval;
+
+ rval = fwnode_property_read_u64_array(fwnode, "link-frequencies",
+ vep->link_frequencies,
+ vep->nr_of_link_frequencies);
+ if (rval < 0)
+ goto out_err;
+
+ return vep;
+
+out_err:
+ v4l2_fwnode_endpoint_free(vep);
+ return ERR_PTR(rval);
+}
+EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse);
+
+/**
+ * v4l2_fwnode_endpoint_parse_link() - parse a link between two endpoints
+ * @__fwnode: pointer to the endpoint's fwnode at the local end of the link
+ * @link: pointer to the V4L2 fwnode link data structure
+ *
+ * Fill the link structure with the local and remote nodes and port numbers.
+ * The local_node and remote_node fields are set to point to the local and
+ * remote port's parent nodes respectively (the port parent node being the
+ * parent node of the port node if that node isn't a 'ports' node, or the
+ * grand-parent node of the port node otherwise).
+ *
+ * A reference is taken to both the local and remote nodes, the caller must use
+ * v4l2_fwnode_endpoint_put_link() to drop the references when done with the
+ * link.
+ *
+ * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be
+ * found.
+ */
+int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode,
+ struct v4l2_fwnode_link *link)
+{
+ const char *port_prop = is_of_node(__fwnode) ? "reg" : "port";
+ struct fwnode_handle *fwnode;
+
+ memset(link, 0, sizeof(*link));
+
+ fwnode = fwnode_get_parent(__fwnode);
+ fwnode_property_read_u32(fwnode, port_prop, &link->local_port);
+ fwnode = fwnode_get_next_parent(fwnode);
+ if (is_of_node(fwnode) &&
+ of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
+ fwnode = fwnode_get_next_parent(fwnode);
+ link->local_node = fwnode;
+
+ fwnode = fwnode_graph_get_remote_endpoint(__fwnode);
+ if (!fwnode) {
+ fwnode_handle_put(fwnode);
+ return -ENOLINK;
+ }
+
+ fwnode = fwnode_get_parent(fwnode);
+ fwnode_property_read_u32(fwnode, port_prop, &link->remote_port);
+ fwnode = fwnode_get_next_parent(fwnode);
+ if (is_of_node(fwnode) &&
+ of_node_cmp(to_of_node(fwnode)->name, "ports") == 0)
+ fwnode = fwnode_get_next_parent(fwnode);
+ link->remote_node = fwnode;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
+
+/**
+ * v4l2_fwnode_put_link() - drop references to nodes in a link
+ * @link: pointer to the V4L2 fwnode link data structure
+ *
+ * Drop references to the local and remote nodes in the link. This function
+ * must be called on every link parsed with v4l2_fwnode_parse_link().
+ */
+void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
+{
+ fwnode_handle_put(link->local_node);
+ fwnode_handle_put(link->remote_node);
+}
+EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index e5a2187381db..cab63bb49c97 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -12,6 +12,7 @@
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*/
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -1229,6 +1230,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_SDR_FMT_CS8: descr = "Complex S8"; break;
case V4L2_SDR_FMT_CS14LE: descr = "Complex S14LE"; break;
case V4L2_SDR_FMT_RU12LE: descr = "Real U12LE"; break;
+ case V4L2_SDR_FMT_PCU16BE: descr = "Planar Complex U16BE"; break;
+ case V4L2_SDR_FMT_PCU18BE: descr = "Planar Complex U18BE"; break;
+ case V4L2_SDR_FMT_PCU20BE: descr = "Planar Complex U20BE"; break;
case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit signed deltas"; break;
case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break;
case V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break;
@@ -2141,6 +2145,47 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
-EINVAL;
}
+/*
+ * The selection API specified originally that the _MPLANE buffer types
+ * shouldn't be used. The reasons for this are lost in the mists of time
+ * (or just really crappy memories). Regardless, this is really annoying
+ * for userspace. So to keep things simple we map _MPLANE buffer types
+ * to their 'regular' counterparts before calling the driver. And we
+ * restore it afterwards. This way applications can use either buffer
+ * type and drivers don't need to check for both.
+ */
+static int v4l_g_selection(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_selection *p = arg;
+ u32 old_type = p->type;
+ int ret;
+
+ if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ p->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ p->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ret = ops->vidioc_g_selection(file, fh, p);
+ p->type = old_type;
+ return ret;
+}
+
+static int v4l_s_selection(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_selection *p = arg;
+ u32 old_type = p->type;
+ int ret;
+
+ if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ p->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ p->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ret = ops->vidioc_s_selection(file, fh, p);
+ p->type = old_type;
+ return ret;
+}
+
static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
@@ -2160,7 +2205,7 @@ static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
else
s.target = V4L2_SEL_TGT_CROP_ACTIVE;
- ret = ops->vidioc_g_selection(file, fh, &s);
+ ret = v4l_g_selection(ops, file, fh, &s);
/* copying results to old structure on success */
if (!ret)
@@ -2187,7 +2232,7 @@ static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
else
s.target = V4L2_SEL_TGT_CROP_ACTIVE;
- return ops->vidioc_s_selection(file, fh, &s);
+ return v4l_s_selection(ops, file, fh, &s);
}
static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
@@ -2229,7 +2274,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
else
s.target = V4L2_SEL_TGT_CROP_BOUNDS;
- ret = ops->vidioc_g_selection(file, fh, &s);
+ ret = v4l_g_selection(ops, file, fh, &s);
if (ret)
return ret;
p->bounds = s.r;
@@ -2240,7 +2285,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
else
s.target = V4L2_SEL_TGT_CROP_DEFAULT;
- ret = ops->vidioc_g_selection(file, fh, &s);
+ ret = v4l_g_selection(ops, file, fh, &s);
if (ret)
return ret;
p->defrect = s.r;
@@ -2472,20 +2517,22 @@ struct v4l2_ioctl_info {
};
/* This control needs a priority check */
-#define INFO_FL_PRIO (1 << 0)
+#define INFO_FL_PRIO (1 << 0)
/* This control can be valid if the filehandle passes a control handler. */
-#define INFO_FL_CTRL (1 << 1)
+#define INFO_FL_CTRL (1 << 1)
/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD (1 << 2)
+#define INFO_FL_STD (1 << 2)
/* This is ioctl has its own function */
-#define INFO_FL_FUNC (1 << 3)
+#define INFO_FL_FUNC (1 << 3)
/* Queuing ioctl */
-#define INFO_FL_QUEUE (1 << 4)
+#define INFO_FL_QUEUE (1 << 4)
+/* Always copy back result, even on error */
+#define INFO_FL_ALWAYS_COPY (1 << 5)
/* Zero struct from after the field to the end */
#define INFO_FL_CLEAR(v4l2_struct, field) \
((offsetof(struct v4l2_struct, field) + \
sizeof(((struct v4l2_struct *)0)->field)) << 16)
-#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
+#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
[_IOC_NR(_ioctl)] = { \
@@ -2536,8 +2583,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0),
- IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY),
IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
@@ -2550,8 +2597,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
- IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
+ IOCTL_INFO_FNC(VIDIOC_G_SELECTION, v4l_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
+ IOCTL_INFO_FNC(VIDIOC_S_SELECTION, v4l_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
@@ -2583,7 +2630,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
- IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
@@ -2801,6 +2848,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
void *parg = (void *)arg;
long err = -EINVAL;
bool has_array_args;
+ bool always_copy = false;
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
@@ -2811,7 +2859,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
parg = sbuf;
} else {
/* too big to allocate from stack */
- mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+ mbuf = kvmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
@@ -2830,8 +2878,10 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
*/
if (v4l2_is_known_ioctl(cmd)) {
u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
+
if (flags & INFO_FL_CLEAR_MASK)
n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+ always_copy = flags & INFO_FL_ALWAYS_COPY;
}
if (copy_from_user(parg, (void __user *)arg, n))
@@ -2858,7 +2908,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
* array) fits into sbuf (so that mbuf will still remain
* unused up to here).
*/
- mbuf = kmalloc(array_size, GFP_KERNEL);
+ mbuf = kvmalloc(array_size, GFP_KERNEL);
err = -ENOMEM;
if (NULL == mbuf)
goto out_array_args;
@@ -2885,9 +2935,11 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
err = -EFAULT;
goto out_array_args;
}
- /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
- results that must be returned. */
- if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
+ /*
+ * Some ioctls can return an error, but still have valid
+ * results that must be returned.
+ */
+ if (err < 0 && !always_copy)
goto out;
out_array_args:
@@ -2901,7 +2953,7 @@ out_array_args:
}
out:
- kfree(mbuf);
+ kvfree(mbuf);
return err;
}
EXPORT_SYMBOL(video_usercopy);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 6bc27e7b2a33..f62e68aa04c4 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -126,6 +126,43 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
}
EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
+void v4l2_m2m_buf_remove_by_buf(struct v4l2_m2m_queue_ctx *q_ctx,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ struct v4l2_m2m_buffer *b;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ b = container_of(vbuf, struct v4l2_m2m_buffer, vb);
+ list_del(&b->list);
+ q_ctx->num_rdy--;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_buf);
+
+struct vb2_v4l2_buffer *
+v4l2_m2m_buf_remove_by_idx(struct v4l2_m2m_queue_ctx *q_ctx, unsigned int idx)
+
+{
+ struct v4l2_m2m_buffer *b, *tmp;
+ struct vb2_v4l2_buffer *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ list_for_each_entry_safe(b, tmp, &q_ctx->rdy_queue, list) {
+ if (b->vb.vb2_buf.index == idx) {
+ list_del(&b->list);
+ q_ctx->num_rdy--;
+ ret = &b->vb;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_idx);
+
/*
* Scheduling handlers
*/
diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
deleted file mode 100644
index 4f59f442dd0a..000000000000
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * V4L2 OF binding parsing library
- *
- * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
- * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
- *
- * Copyright (C) 2012 Renesas Electronics Corp.
- * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/types.h>
-
-#include <media/v4l2-of.h>
-
-static int v4l2_of_parse_csi_bus(const struct device_node *node,
- struct v4l2_of_endpoint *endpoint)
-{
- struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2;
- struct property *prop;
- bool have_clk_lane = false;
- unsigned int flags = 0, lanes_used = 0;
- u32 v;
-
- prop = of_find_property(node, "data-lanes", NULL);
- if (prop) {
- const __be32 *lane = NULL;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(bus->data_lanes); i++) {
- lane = of_prop_next_u32(prop, lane, &v);
- if (!lane)
- break;
-
- if (lanes_used & BIT(v))
- pr_warn("%s: duplicated lane %u in data-lanes\n",
- node->full_name, v);
- lanes_used |= BIT(v);
-
- bus->data_lanes[i] = v;
- }
- bus->num_data_lanes = i;
- }
-
- prop = of_find_property(node, "lane-polarities", NULL);
- if (prop) {
- const __be32 *polarity = NULL;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(bus->lane_polarities); i++) {
- polarity = of_prop_next_u32(prop, polarity, &v);
- if (!polarity)
- break;
- bus->lane_polarities[i] = v;
- }
-
- if (i < 1 + bus->num_data_lanes /* clock + data */) {
- pr_warn("%s: too few lane-polarities entries (need %u, got %u)\n",
- node->full_name, 1 + bus->num_data_lanes, i);
- return -EINVAL;
- }
- }
-
- if (!of_property_read_u32(node, "clock-lanes", &v)) {
- if (lanes_used & BIT(v))
- pr_warn("%s: duplicated lane %u in clock-lanes\n",
- node->full_name, v);
- lanes_used |= BIT(v);
-
- bus->clock_lane = v;
- have_clk_lane = true;
- }
-
- if (of_get_property(node, "clock-noncontinuous", &v))
- flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
- else if (have_clk_lane || bus->num_data_lanes > 0)
- flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
-
- bus->flags = flags;
- endpoint->bus_type = V4L2_MBUS_CSI2;
-
- return 0;
-}
-
-static void v4l2_of_parse_parallel_bus(const struct device_node *node,
- struct v4l2_of_endpoint *endpoint)
-{
- struct v4l2_of_bus_parallel *bus = &endpoint->bus.parallel;
- unsigned int flags = 0;
- u32 v;
-
- if (!of_property_read_u32(node, "hsync-active", &v))
- flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
- V4L2_MBUS_HSYNC_ACTIVE_LOW;
-
- if (!of_property_read_u32(node, "vsync-active", &v))
- flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH :
- V4L2_MBUS_VSYNC_ACTIVE_LOW;
-
- if (!of_property_read_u32(node, "field-even-active", &v))
- flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH :
- V4L2_MBUS_FIELD_EVEN_LOW;
- if (flags)
- endpoint->bus_type = V4L2_MBUS_PARALLEL;
- else
- endpoint->bus_type = V4L2_MBUS_BT656;
-
- if (!of_property_read_u32(node, "pclk-sample", &v))
- flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
- V4L2_MBUS_PCLK_SAMPLE_FALLING;
-
- if (!of_property_read_u32(node, "data-active", &v))
- flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH :
- V4L2_MBUS_DATA_ACTIVE_LOW;
-
- if (of_get_property(node, "slave-mode", &v))
- flags |= V4L2_MBUS_SLAVE;
- else
- flags |= V4L2_MBUS_MASTER;
-
- if (!of_property_read_u32(node, "bus-width", &v))
- bus->bus_width = v;
-
- if (!of_property_read_u32(node, "data-shift", &v))
- bus->data_shift = v;
-
- if (!of_property_read_u32(node, "sync-on-green-active", &v))
- flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH :
- V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW;
-
- bus->flags = flags;
-
-}
-
-/**
- * v4l2_of_parse_endpoint() - parse all endpoint node properties
- * @node: pointer to endpoint device_node
- * @endpoint: pointer to the V4L2 OF endpoint data structure
- *
- * All properties are optional. If none are found, we don't set any flags.
- * This means the port has a static configuration and no properties have
- * to be specified explicitly.
- * If any properties that identify the bus as parallel are found and
- * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise
- * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the
- * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag.
- * The caller should hold a reference to @node.
- *
- * NOTE: This function does not parse properties the size of which is
- * variable without a low fixed limit. Please use
- * v4l2_of_alloc_parse_endpoint() in new drivers instead.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int v4l2_of_parse_endpoint(const struct device_node *node,
- struct v4l2_of_endpoint *endpoint)
-{
- int rval;
-
- of_graph_parse_endpoint(node, &endpoint->base);
- /* Zero fields from bus_type to until the end */
- memset(&endpoint->bus_type, 0, sizeof(*endpoint) -
- offsetof(typeof(*endpoint), bus_type));
-
- rval = v4l2_of_parse_csi_bus(node, endpoint);
- if (rval)
- return rval;
- /*
- * Parse the parallel video bus properties only if none
- * of the MIPI CSI-2 specific properties were found.
- */
- if (endpoint->bus.mipi_csi2.flags == 0)
- v4l2_of_parse_parallel_bus(node, endpoint);
-
- return 0;
-}
-EXPORT_SYMBOL(v4l2_of_parse_endpoint);
-
-/*
- * v4l2_of_free_endpoint() - free the endpoint acquired by
- * v4l2_of_alloc_parse_endpoint()
- * @endpoint - the endpoint the resources of which are to be released
- *
- * It is safe to call this function with NULL argument or on an
- * endpoint the parsing of which failed.
- */
-void v4l2_of_free_endpoint(struct v4l2_of_endpoint *endpoint)
-{
- if (IS_ERR_OR_NULL(endpoint))
- return;
-
- kfree(endpoint->link_frequencies);
- kfree(endpoint);
-}
-EXPORT_SYMBOL(v4l2_of_free_endpoint);
-
-/**
- * v4l2_of_alloc_parse_endpoint() - parse all endpoint node properties
- * @node: pointer to endpoint device_node
- *
- * All properties are optional. If none are found, we don't set any flags.
- * This means the port has a static configuration and no properties have
- * to be specified explicitly.
- * If any properties that identify the bus as parallel are found and
- * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise
- * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the
- * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag.
- * The caller should hold a reference to @node.
- *
- * v4l2_of_alloc_parse_endpoint() has two important differences to
- * v4l2_of_parse_endpoint():
- *
- * 1. It also parses variable size data and
- *
- * 2. The memory it has allocated to store the variable size data must
- * be freed using v4l2_of_free_endpoint() when no longer needed.
- *
- * Return: Pointer to v4l2_of_endpoint if successful, on error a
- * negative error code.
- */
-struct v4l2_of_endpoint *v4l2_of_alloc_parse_endpoint(
- const struct device_node *node)
-{
- struct v4l2_of_endpoint *endpoint;
- int len;
- int rval;
-
- endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
- if (!endpoint)
- return ERR_PTR(-ENOMEM);
-
- rval = v4l2_of_parse_endpoint(node, endpoint);
- if (rval < 0)
- goto out_err;
-
- if (of_get_property(node, "link-frequencies", &len)) {
- endpoint->link_frequencies = kmalloc(len, GFP_KERNEL);
- if (!endpoint->link_frequencies) {
- rval = -ENOMEM;
- goto out_err;
- }
-
- endpoint->nr_of_link_frequencies =
- len / sizeof(*endpoint->link_frequencies);
-
- rval = of_property_read_u64_array(
- node, "link-frequencies", endpoint->link_frequencies,
- endpoint->nr_of_link_frequencies);
- if (rval < 0)
- goto out_err;
- }
-
- return endpoint;
-
-out_err:
- v4l2_of_free_endpoint(endpoint);
- return ERR_PTR(rval);
-}
-EXPORT_SYMBOL(v4l2_of_alloc_parse_endpoint);
-
-/**
- * v4l2_of_parse_link() - parse a link between two endpoints
- * @node: pointer to the endpoint at the local end of the link
- * @link: pointer to the V4L2 OF link data structure
- *
- * Fill the link structure with the local and remote nodes and port numbers.
- * The local_node and remote_node fields are set to point to the local and
- * remote port's parent nodes respectively (the port parent node being the
- * parent node of the port node if that node isn't a 'ports' node, or the
- * grand-parent node of the port node otherwise).
- *
- * A reference is taken to both the local and remote nodes, the caller must use
- * v4l2_of_put_link() to drop the references when done with the link.
- *
- * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found.
- */
-int v4l2_of_parse_link(const struct device_node *node,
- struct v4l2_of_link *link)
-{
- struct device_node *np;
-
- memset(link, 0, sizeof(*link));
-
- np = of_get_parent(node);
- of_property_read_u32(np, "reg", &link->local_port);
- np = of_get_next_parent(np);
- if (of_node_cmp(np->name, "ports") == 0)
- np = of_get_next_parent(np);
- link->local_node = np;
-
- np = of_parse_phandle(node, "remote-endpoint", 0);
- if (!np) {
- of_node_put(link->local_node);
- return -ENOLINK;
- }
-
- np = of_get_parent(np);
- of_property_read_u32(np, "reg", &link->remote_port);
- np = of_get_next_parent(np);
- if (of_node_cmp(np->name, "ports") == 0)
- np = of_get_next_parent(np);
- link->remote_node = np;
-
- return 0;
-}
-EXPORT_SYMBOL(v4l2_of_parse_link);
-
-/**
- * v4l2_of_put_link() - drop references to nodes in a link
- * @link: pointer to the V4L2 OF link data structure
- *
- * Drop references to the local and remote nodes in the link. This function must
- * be called on every link parsed with v4l2_of_parse_link().
- */
-void v4l2_of_put_link(struct v4l2_of_link *link)
-{
- of_node_put(link->local_node);
- of_node_put(link->remote_node);
-}
-EXPORT_SYMBOL(v4l2_of_put_link);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index da78497ae5ed..43fefa73e0a3 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -17,6 +17,7 @@
*/
#include <linux/ioctl.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/videodev2.h>
@@ -577,13 +578,14 @@ v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd)
if (!sd->entity.num_pads)
return NULL;
- cfg = kcalloc(sd->entity.num_pads, sizeof(*cfg), GFP_KERNEL);
+ cfg = kvmalloc_array(sd->entity.num_pads, sizeof(*cfg),
+ GFP_KERNEL | __GFP_ZERO);
if (!cfg)
return NULL;
ret = v4l2_subdev_call(sd, pad, init_cfg, cfg);
if (ret < 0 && ret != -ENOIOCTLCMD) {
- kfree(cfg);
+ kvfree(cfg);
return NULL;
}
@@ -593,7 +595,7 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_pad_config);
void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg)
{
- kfree(cfg);
+ kvfree(cfg);
}
EXPORT_SYMBOL_GPL(v4l2_subdev_free_pad_config);
#endif /* CONFIG_MEDIA_CONTROLLER */
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index c0175ea7e7ad..14f83cecfa92 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -210,7 +210,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
mem_priv = call_ptr_memop(vb, alloc,
q->alloc_devs[plane] ? : q->dev,
q->dma_attrs, size, dma_dir, q->gfp_flags);
- if (IS_ERR(mem_priv)) {
+ if (IS_ERR_OR_NULL(mem_priv)) {
if (mem_priv)
ret = PTR_ERR(mem_priv);
goto free;
@@ -956,9 +956,9 @@ void vb2_discard_done(struct vb2_queue *q)
EXPORT_SYMBOL_GPL(vb2_discard_done);
/**
- * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ * __prepare_mmap() - prepare an MMAP buffer
*/
-static int __qbuf_mmap(struct vb2_buffer *vb, const void *pb)
+static int __prepare_mmap(struct vb2_buffer *vb, const void *pb)
{
int ret = 0;
@@ -969,9 +969,9 @@ static int __qbuf_mmap(struct vb2_buffer *vb, const void *pb)
}
/**
- * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ * __prepare_userptr() - prepare a USERPTR buffer
*/
-static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb)
+static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
{
struct vb2_plane planes[VB2_MAX_PLANES];
struct vb2_queue *q = vb->vb2_queue;
@@ -1087,9 +1087,9 @@ err:
}
/**
- * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer
+ * __prepare_dmabuf() - prepare a DMABUF buffer
*/
-static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb)
+static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb)
{
struct vb2_plane planes[VB2_MAX_PLANES];
struct vb2_queue *q = vb->vb2_queue;
@@ -1227,23 +1227,19 @@ err:
static void __enqueue_in_driver(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
- unsigned int plane;
vb->state = VB2_BUF_STATE_ACTIVE;
atomic_inc(&q->owned_by_drv_count);
trace_vb2_buf_queue(q, vb);
- /* sync buffers */
- for (plane = 0; plane < vb->num_planes; ++plane)
- call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
-
call_void_vb_qop(vb, buf_queue, vb);
}
static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
{
struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
int ret;
if (q->error) {
@@ -1255,24 +1251,32 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
switch (q->memory) {
case VB2_MEMORY_MMAP:
- ret = __qbuf_mmap(vb, pb);
+ ret = __prepare_mmap(vb, pb);
break;
case VB2_MEMORY_USERPTR:
- ret = __qbuf_userptr(vb, pb);
+ ret = __prepare_userptr(vb, pb);
break;
case VB2_MEMORY_DMABUF:
- ret = __qbuf_dmabuf(vb, pb);
+ ret = __prepare_dmabuf(vb, pb);
break;
default:
WARN(1, "Invalid queue type\n");
ret = -EINVAL;
}
- if (ret)
+ if (ret) {
dprintk(1, "buffer preparation failed: %d\n", ret);
- vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ return ret;
+ }
- return ret;
+ /* sync buffers */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
+
+ vb->state = VB2_BUF_STATE_PREPARED;
+
+ return 0;
}
int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 8e8798a74760..5defa1f22ca2 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -120,8 +120,8 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs,
buf->num_pages = size >> PAGE_SHIFT;
buf->dma_sgt = &buf->sg_table;
- buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
- GFP_KERNEL);
+ buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *),
+ GFP_KERNEL | __GFP_ZERO);
if (!buf->pages)
goto fail_pages_array_alloc;
@@ -165,7 +165,7 @@ fail_table_alloc:
while (num_pages--)
__free_page(buf->pages[num_pages]);
fail_pages_alloc:
- kfree(buf->pages);
+ kvfree(buf->pages);
fail_pages_array_alloc:
kfree(buf);
return ERR_PTR(-ENOMEM);
@@ -187,7 +187,7 @@ static void vb2_dma_sg_put(void *buf_priv)
sg_free_table(buf->dma_sgt);
while (--i >= 0)
__free_page(buf->pages[i]);
- kfree(buf->pages);
+ kvfree(buf->pages);
put_device(buf->dev);
kfree(buf);
}