From e09b41010ba33a20a87472ee821fa407a5b8da36 Mon Sep 17 00:00:00 2001 From: José Pekkarinen Date: Mon, 11 Apr 2016 10:41:07 +0300 Subject: These changes are the raw update to linux-4.4.6-rt14. Kernel sources are taken from kernel.org, and rt patch from the rt wiki download page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen --- kernel/drivers/media/Kconfig | 2 +- kernel/drivers/media/common/b2c2/Kconfig | 1 + kernel/drivers/media/common/b2c2/flexcop-common.h | 1 + .../drivers/media/common/b2c2/flexcop-fe-tuner.c | 63 +- .../drivers/media/common/b2c2/flexcop-hw-filter.c | 16 +- kernel/drivers/media/common/b2c2/flexcop-misc.c | 1 + kernel/drivers/media/common/b2c2/flexcop-reg.h | 1 + kernel/drivers/media/common/saa7146/saa7146_hlp.c | 9 +- kernel/drivers/media/common/siano/smscoreapi.h | 3 +- kernel/drivers/media/common/siano/smsdvb-main.c | 6 +- kernel/drivers/media/common/siano/smsdvb.h | 2 +- kernel/drivers/media/common/siano/smsir.c | 2 +- kernel/drivers/media/dvb-core/demux.h | 619 ++++- kernel/drivers/media/dvb-core/dmxdev.c | 10 +- kernel/drivers/media/dvb-core/dvb-usb-ids.h | 1 + kernel/drivers/media/dvb-core/dvb_ca_en50221.c | 167 +- kernel/drivers/media/dvb-core/dvb_ca_en50221.h | 133 +- kernel/drivers/media/dvb-core/dvb_demux.c | 11 +- kernel/drivers/media/dvb-core/dvb_frontend.c | 85 +- kernel/drivers/media/dvb-core/dvb_frontend.h | 453 +++- kernel/drivers/media/dvb-core/dvb_math.h | 25 +- kernel/drivers/media/dvb-core/dvb_net.c | 7 +- kernel/drivers/media/dvb-core/dvb_ringbuffer.h | 135 +- kernel/drivers/media/dvb-core/dvbdev.h | 112 +- kernel/drivers/media/dvb-frontends/Kconfig | 43 +- kernel/drivers/media/dvb-frontends/Makefile | 5 + kernel/drivers/media/dvb-frontends/a8293.c | 175 +- kernel/drivers/media/dvb-frontends/a8293.h | 31 +- kernel/drivers/media/dvb-frontends/af9013.c | 4 +- kernel/drivers/media/dvb-frontends/af9033.c | 5 +- kernel/drivers/media/dvb-frontends/as102_fe.c | 4 +- kernel/drivers/media/dvb-frontends/ascot2e.c | 548 ++++ kernel/drivers/media/dvb-frontends/ascot2e.h | 58 + kernel/drivers/media/dvb-frontends/atbm8830.c | 3 +- .../drivers/media/dvb-frontends/au8522_decoder.c | 1 - kernel/drivers/media/dvb-frontends/au8522_dig.c | 6 +- kernel/drivers/media/dvb-frontends/au8522_priv.h | 2 +- kernel/drivers/media/dvb-frontends/bcm3510.c | 6 +- kernel/drivers/media/dvb-frontends/cx22700.c | 9 +- kernel/drivers/media/dvb-frontends/cx22702.c | 2 +- kernel/drivers/media/dvb-frontends/cx24110.c | 19 +- kernel/drivers/media/dvb-frontends/cx24116.c | 38 +- kernel/drivers/media/dvb-frontends/cx24117.c | 40 +- kernel/drivers/media/dvb-frontends/cx24120.c | 1595 ++++++++++++ kernel/drivers/media/dvb-frontends/cx24120.h | 58 + kernel/drivers/media/dvb-frontends/cx24123.c | 20 +- kernel/drivers/media/dvb-frontends/cx24123.h | 2 +- kernel/drivers/media/dvb-frontends/cxd2820r_c.c | 2 +- kernel/drivers/media/dvb-frontends/cxd2820r_core.c | 5 +- kernel/drivers/media/dvb-frontends/cxd2820r_priv.h | 8 +- kernel/drivers/media/dvb-frontends/cxd2820r_t.c | 2 +- kernel/drivers/media/dvb-frontends/cxd2820r_t2.c | 2 +- kernel/drivers/media/dvb-frontends/cxd2841er.c | 2727 ++++++++++++++++++++ kernel/drivers/media/dvb-frontends/cxd2841er.h | 65 + .../drivers/media/dvb-frontends/cxd2841er_priv.h | 43 + kernel/drivers/media/dvb-frontends/dib0070.c | 575 ++--- kernel/drivers/media/dvb-frontends/dib0090.c | 4 +- kernel/drivers/media/dvb-frontends/dib3000mb.c | 7 +- kernel/drivers/media/dvb-frontends/dib3000mc.c | 20 +- kernel/drivers/media/dvb-frontends/dib7000m.c | 2 +- kernel/drivers/media/dvb-frontends/dib7000p.c | 6 +- kernel/drivers/media/dvb-frontends/dib8000.c | 10 +- kernel/drivers/media/dvb-frontends/dib8000.h | 2 +- kernel/drivers/media/dvb-frontends/dib9000.c | 4 +- kernel/drivers/media/dvb-frontends/drx39xyj/drxj.c | 42 +- kernel/drivers/media/dvb-frontends/drxd_hard.c | 5 +- kernel/drivers/media/dvb-frontends/drxk_hard.c | 11 +- kernel/drivers/media/dvb-frontends/drxk_hard.h | 2 +- kernel/drivers/media/dvb-frontends/ds3000.c | 13 +- kernel/drivers/media/dvb-frontends/dvb-pll.c | 50 +- kernel/drivers/media/dvb-frontends/dvb_dummy_fe.c | 9 +- kernel/drivers/media/dvb-frontends/ec100.c | 2 +- kernel/drivers/media/dvb-frontends/hd29l2.c | 2 +- kernel/drivers/media/dvb-frontends/hd29l2_priv.h | 2 +- kernel/drivers/media/dvb-frontends/horus3a.c | 430 +++ kernel/drivers/media/dvb-frontends/horus3a.h | 58 + kernel/drivers/media/dvb-frontends/isl6405.c | 3 +- kernel/drivers/media/dvb-frontends/isl6421.c | 6 +- kernel/drivers/media/dvb-frontends/l64781.c | 2 +- kernel/drivers/media/dvb-frontends/lg2160.c | 2 +- kernel/drivers/media/dvb-frontends/lgdt3305.c | 4 +- kernel/drivers/media/dvb-frontends/lgdt3306a.c | 11 +- kernel/drivers/media/dvb-frontends/lgdt330x.c | 8 +- kernel/drivers/media/dvb-frontends/lgs8gl5.c | 2 +- kernel/drivers/media/dvb-frontends/lgs8gxx.c | 3 +- kernel/drivers/media/dvb-frontends/lnbh25.c | 189 ++ kernel/drivers/media/dvb-frontends/lnbh25.h | 56 + kernel/drivers/media/dvb-frontends/lnbp21.c | 4 +- kernel/drivers/media/dvb-frontends/lnbp22.c | 3 +- kernel/drivers/media/dvb-frontends/m88ds3103.c | 1267 ++++----- kernel/drivers/media/dvb-frontends/m88ds3103.h | 67 +- .../drivers/media/dvb-frontends/m88ds3103_priv.h | 20 +- kernel/drivers/media/dvb-frontends/m88rs2000.c | 19 +- kernel/drivers/media/dvb-frontends/mb86a16.c | 7 +- kernel/drivers/media/dvb-frontends/mb86a16.h | 3 +- kernel/drivers/media/dvb-frontends/mb86a20s.c | 6 +- kernel/drivers/media/dvb-frontends/mb86a20s.h | 2 +- kernel/drivers/media/dvb-frontends/mt312.c | 17 +- kernel/drivers/media/dvb-frontends/mt352.c | 2 +- kernel/drivers/media/dvb-frontends/nxt200x.c | 2 +- kernel/drivers/media/dvb-frontends/nxt6000.c | 12 +- kernel/drivers/media/dvb-frontends/or51132.c | 6 +- kernel/drivers/media/dvb-frontends/or51211.c | 2 +- kernel/drivers/media/dvb-frontends/rtl2830.c | 3 +- kernel/drivers/media/dvb-frontends/rtl2830_priv.h | 2 +- kernel/drivers/media/dvb-frontends/rtl2832.c | 11 +- kernel/drivers/media/dvb-frontends/rtl2832.h | 2 + kernel/drivers/media/dvb-frontends/rtl2832_priv.h | 51 +- kernel/drivers/media/dvb-frontends/rtl2832_sdr.c | 144 +- kernel/drivers/media/dvb-frontends/rtl2832_sdr.h | 1 + kernel/drivers/media/dvb-frontends/s5h1409.c | 6 +- kernel/drivers/media/dvb-frontends/s5h1411.c | 6 +- kernel/drivers/media/dvb-frontends/s5h1420.c | 41 +- kernel/drivers/media/dvb-frontends/s5h1432.c | 4 +- kernel/drivers/media/dvb-frontends/s921.c | 8 +- kernel/drivers/media/dvb-frontends/s921.h | 2 +- kernel/drivers/media/dvb-frontends/si2165.c | 2 +- kernel/drivers/media/dvb-frontends/si2168.c | 149 +- kernel/drivers/media/dvb-frontends/si2168.h | 3 + kernel/drivers/media/dvb-frontends/si2168_priv.h | 6 +- kernel/drivers/media/dvb-frontends/si21xx.c | 10 +- kernel/drivers/media/dvb-frontends/sp2.c | 1 - kernel/drivers/media/dvb-frontends/sp8870.c | 3 +- kernel/drivers/media/dvb-frontends/sp887x.c | 2 +- kernel/drivers/media/dvb-frontends/stb0899_drv.c | 8 +- kernel/drivers/media/dvb-frontends/stv0288.c | 39 +- kernel/drivers/media/dvb-frontends/stv0297.c | 19 +- kernel/drivers/media/dvb-frontends/stv0299.c | 34 +- kernel/drivers/media/dvb-frontends/stv0367.c | 29 +- kernel/drivers/media/dvb-frontends/stv0367_priv.h | 2 +- kernel/drivers/media/dvb-frontends/stv0900_core.c | 6 +- kernel/drivers/media/dvb-frontends/stv0900_sw.c | 6 +- kernel/drivers/media/dvb-frontends/stv090x.c | 5 +- kernel/drivers/media/dvb-frontends/stv6110.c | 2 +- kernel/drivers/media/dvb-frontends/tc90522.c | 17 +- kernel/drivers/media/dvb-frontends/tda10021.c | 9 +- kernel/drivers/media/dvb-frontends/tda10023.c | 5 +- kernel/drivers/media/dvb-frontends/tda10048.c | 2 +- kernel/drivers/media/dvb-frontends/tda1004x.c | 14 +- kernel/drivers/media/dvb-frontends/tda10071.c | 904 +++---- kernel/drivers/media/dvb-frontends/tda10071.h | 70 +- kernel/drivers/media/dvb-frontends/tda10071_priv.h | 31 +- kernel/drivers/media/dvb-frontends/tda10086.c | 13 +- kernel/drivers/media/dvb-frontends/tda8083.c | 38 +- kernel/drivers/media/dvb-frontends/ts2020.c | 592 +++-- kernel/drivers/media/dvb-frontends/ts2020.h | 17 +- kernel/drivers/media/dvb-frontends/ves1820.c | 6 +- kernel/drivers/media/dvb-frontends/ves1x93.c | 15 +- kernel/drivers/media/dvb-frontends/zl10353.c | 12 +- kernel/drivers/media/firewire/firedtv-fe.c | 8 +- kernel/drivers/media/firewire/firedtv.h | 4 +- kernel/drivers/media/i2c/Kconfig | 15 +- kernel/drivers/media/i2c/Makefile | 1 + kernel/drivers/media/i2c/adp1653.c | 100 +- kernel/drivers/media/i2c/adv7170.c | 43 +- kernel/drivers/media/i2c/adv7175.c | 43 +- kernel/drivers/media/i2c/adv7180.c | 12 +- kernel/drivers/media/i2c/adv7183.c | 61 +- kernel/drivers/media/i2c/adv7343.c | 8 - kernel/drivers/media/i2c/adv7393.c | 7 - kernel/drivers/media/i2c/adv7511.c | 163 +- kernel/drivers/media/i2c/adv7604.c | 673 +++-- kernel/drivers/media/i2c/adv7842.c | 329 ++- kernel/drivers/media/i2c/ak881x.c | 47 +- kernel/drivers/media/i2c/bt819.c | 12 - kernel/drivers/media/i2c/bt856.c | 1 - kernel/drivers/media/i2c/bt866.c | 1 - kernel/drivers/media/i2c/cs5345.c | 8 - kernel/drivers/media/i2c/cs53l32a.c | 1 - kernel/drivers/media/i2c/cx25840/cx25840-core.c | 18 +- kernel/drivers/media/i2c/ks0127.c | 1 - kernel/drivers/media/i2c/m52790.c | 1 - kernel/drivers/media/i2c/ml86v7667.c | 40 +- kernel/drivers/media/i2c/msp3400-driver.c | 1 - kernel/drivers/media/i2c/mt9v011.c | 54 +- kernel/drivers/media/i2c/mt9v032.c | 2 +- kernel/drivers/media/i2c/ov2659.c | 42 +- kernel/drivers/media/i2c/ov7640.c | 1 - kernel/drivers/media/i2c/ov7670.c | 66 +- kernel/drivers/media/i2c/ov9650.c | 4 +- kernel/drivers/media/i2c/s5c73m3/s5c73m3-core.c | 4 +- kernel/drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 3 +- kernel/drivers/media/i2c/s5k5baf.c | 4 +- kernel/drivers/media/i2c/s5k6a3.c | 1 + kernel/drivers/media/i2c/s5k6aa.c | 2 +- kernel/drivers/media/i2c/saa6588.c | 5 +- kernel/drivers/media/i2c/saa6752hs.c | 43 +- kernel/drivers/media/i2c/saa7110.c | 12 - kernel/drivers/media/i2c/saa7115.c | 17 +- kernel/drivers/media/i2c/saa7127.c | 1 - kernel/drivers/media/i2c/saa717x.c | 28 +- kernel/drivers/media/i2c/saa7185.c | 1 - kernel/drivers/media/i2c/smiapp/smiapp-core.c | 39 +- kernel/drivers/media/i2c/soc_camera/imx074.c | 66 +- kernel/drivers/media/i2c/soc_camera/mt9m001.c | 43 +- kernel/drivers/media/i2c/soc_camera/mt9m111.c | 57 +- kernel/drivers/media/i2c/soc_camera/mt9t031.c | 74 +- kernel/drivers/media/i2c/soc_camera/mt9t112.c | 49 +- kernel/drivers/media/i2c/soc_camera/mt9v022.c | 43 +- kernel/drivers/media/i2c/soc_camera/ov2640.c | 62 +- kernel/drivers/media/i2c/soc_camera/ov5642.c | 60 +- kernel/drivers/media/i2c/soc_camera/ov6650.c | 43 +- kernel/drivers/media/i2c/soc_camera/ov772x.c | 41 +- kernel/drivers/media/i2c/soc_camera/ov9640.c | 32 +- kernel/drivers/media/i2c/soc_camera/ov9740.c | 35 +- kernel/drivers/media/i2c/soc_camera/rj54n1cb0c.c | 66 +- kernel/drivers/media/i2c/soc_camera/tw9910.c | 76 +- kernel/drivers/media/i2c/sony-btf-mpx.c | 1 - kernel/drivers/media/i2c/sr030pc30.c | 77 +- kernel/drivers/media/i2c/tc358743.c | 1979 ++++++++++++++ kernel/drivers/media/i2c/tc358743_regs.h | 681 +++++ kernel/drivers/media/i2c/tda7432.c | 8 - kernel/drivers/media/i2c/tda9840.c | 1 - kernel/drivers/media/i2c/tea6415c.c | 1 - kernel/drivers/media/i2c/tea6420.c | 1 - kernel/drivers/media/i2c/ths7303.c | 1 - kernel/drivers/media/i2c/tlv320aic23b.c | 7 - kernel/drivers/media/i2c/tvaudio.c | 3 +- kernel/drivers/media/i2c/tvp514x.c | 66 +- kernel/drivers/media/i2c/tvp5150.c | 45 +- kernel/drivers/media/i2c/tvp7002.c | 55 - kernel/drivers/media/i2c/tw9903.c | 1 - kernel/drivers/media/i2c/tw9906.c | 1 - kernel/drivers/media/i2c/upd64031a.c | 1 - kernel/drivers/media/i2c/upd64083.c | 1 - kernel/drivers/media/i2c/vp27smpx.c | 1 - kernel/drivers/media/i2c/vpx3220.c | 8 - kernel/drivers/media/i2c/vs6624.c | 55 +- kernel/drivers/media/i2c/wm8739.c | 8 - kernel/drivers/media/i2c/wm8775.c | 1 - kernel/drivers/media/media-entity.c | 10 +- kernel/drivers/media/pci/Kconfig | 9 +- kernel/drivers/media/pci/Makefile | 5 +- kernel/drivers/media/pci/bt8xx/btcx-risc.c | 5 +- kernel/drivers/media/pci/bt8xx/bttv-audio-hook.c | 443 ++-- kernel/drivers/media/pci/bt8xx/bttv-driver.c | 10 +- kernel/drivers/media/pci/bt8xx/bttv-input.c | 21 +- kernel/drivers/media/pci/bt8xx/bttvp.h | 2 +- kernel/drivers/media/pci/bt8xx/dst.c | 25 +- kernel/drivers/media/pci/bt8xx/dst_ca.c | 138 +- kernel/drivers/media/pci/bt8xx/dst_common.h | 12 +- kernel/drivers/media/pci/cobalt/Kconfig | 20 + kernel/drivers/media/pci/cobalt/Makefile | 5 + kernel/drivers/media/pci/cobalt/cobalt-alsa-main.c | 162 ++ kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.c | 603 +++++ kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.h | 22 + kernel/drivers/media/pci/cobalt/cobalt-alsa.h | 41 + kernel/drivers/media/pci/cobalt/cobalt-cpld.c | 339 +++ kernel/drivers/media/pci/cobalt/cobalt-cpld.h | 29 + kernel/drivers/media/pci/cobalt/cobalt-driver.c | 833 ++++++ kernel/drivers/media/pci/cobalt/cobalt-driver.h | 382 +++ kernel/drivers/media/pci/cobalt/cobalt-flash.c | 128 + kernel/drivers/media/pci/cobalt/cobalt-flash.h | 29 + kernel/drivers/media/pci/cobalt/cobalt-i2c.c | 396 +++ kernel/drivers/media/pci/cobalt/cobalt-i2c.h | 25 + kernel/drivers/media/pci/cobalt/cobalt-irq.c | 259 ++ kernel/drivers/media/pci/cobalt/cobalt-irq.h | 25 + kernel/drivers/media/pci/cobalt/cobalt-omnitek.c | 341 +++ kernel/drivers/media/pci/cobalt/cobalt-omnitek.h | 62 + kernel/drivers/media/pci/cobalt/cobalt-v4l2.c | 1279 +++++++++ kernel/drivers/media/pci/cobalt/cobalt-v4l2.h | 22 + .../cobalt/m00233_video_measure_memmap_package.h | 115 + .../pci/cobalt/m00235_fdma_packer_memmap_package.h | 44 + .../media/pci/cobalt/m00389_cvi_memmap_package.h | 59 + .../media/pci/cobalt/m00460_evcnt_memmap_package.h | 44 + .../pci/cobalt/m00473_freewheel_memmap_package.h | 57 + .../m00479_clk_loss_detector_memmap_package.h | 53 + .../m00514_syncgen_flow_evcnt_memmap_package.h | 88 + kernel/drivers/media/pci/cx18/cx18-av-core.c | 16 +- kernel/drivers/media/pci/cx18/cx18-controls.c | 13 +- kernel/drivers/media/pci/cx18/cx18-driver.c | 4 +- kernel/drivers/media/pci/cx18/cx18-ioctl.c | 12 +- kernel/drivers/media/pci/cx18/cx18-mailbox.c | 2 +- kernel/drivers/media/pci/cx23885/altera-ci.c | 2 +- kernel/drivers/media/pci/cx23885/cx23885-417.c | 13 +- kernel/drivers/media/pci/cx23885/cx23885-core.c | 28 +- kernel/drivers/media/pci/cx23885/cx23885-dvb.c | 161 +- kernel/drivers/media/pci/cx23885/cx23885-f300.c | 2 +- kernel/drivers/media/pci/cx23885/cx23885-f300.h | 2 +- kernel/drivers/media/pci/cx23885/cx23885-vbi.c | 18 +- kernel/drivers/media/pci/cx23885/cx23885-video.c | 41 +- kernel/drivers/media/pci/cx23885/cx23885.h | 5 +- kernel/drivers/media/pci/cx25821/cx25821-alsa.c | 2 +- kernel/drivers/media/pci/cx25821/cx25821-core.c | 3 +- .../drivers/media/pci/cx25821/cx25821-medusa-reg.h | 6 +- kernel/drivers/media/pci/cx25821/cx25821-video.c | 24 +- kernel/drivers/media/pci/cx25821/cx25821.h | 3 +- kernel/drivers/media/pci/cx88/cx88-alsa.c | 6 +- kernel/drivers/media/pci/cx88/cx88-blackbird.c | 15 +- kernel/drivers/media/pci/cx88/cx88-core.c | 6 +- kernel/drivers/media/pci/cx88/cx88-dvb.c | 25 +- kernel/drivers/media/pci/cx88/cx88-mpeg.c | 23 +- kernel/drivers/media/pci/cx88/cx88-vbi.c | 25 +- kernel/drivers/media/pci/cx88/cx88-video.c | 34 +- kernel/drivers/media/pci/cx88/cx88.h | 8 +- kernel/drivers/media/pci/ddbridge/ddbridge-core.c | 3 +- kernel/drivers/media/pci/dm1105/dm1105.c | 3 +- kernel/drivers/media/pci/dt3155/Kconfig | 13 + kernel/drivers/media/pci/dt3155/Makefile | 1 + kernel/drivers/media/pci/dt3155/dt3155.c | 633 +++++ kernel/drivers/media/pci/dt3155/dt3155.h | 197 ++ kernel/drivers/media/pci/ivtv/Kconfig | 3 + kernel/drivers/media/pci/ivtv/ivtv-alsa-main.c | 14 +- kernel/drivers/media/pci/ivtv/ivtv-controls.c | 12 +- kernel/drivers/media/pci/ivtv/ivtv-driver.h | 3 +- kernel/drivers/media/pci/ivtv/ivtv-gpio.c | 7 - kernel/drivers/media/pci/ivtv/ivtv-ioctl.c | 15 +- kernel/drivers/media/pci/ivtv/ivtv-yuv.c | 12 +- kernel/drivers/media/pci/ivtv/ivtvfb.c | 61 +- kernel/drivers/media/pci/mantis/hopper_cards.c | 14 +- kernel/drivers/media/pci/mantis/mantis_cards.c | 94 +- kernel/drivers/media/pci/mantis/mantis_common.h | 33 +- kernel/drivers/media/pci/mantis/mantis_dma.c | 14 +- kernel/drivers/media/pci/mantis/mantis_i2c.c | 12 +- kernel/drivers/media/pci/mantis/mantis_input.c | 110 +- kernel/drivers/media/pci/mantis/mantis_input.h | 24 + kernel/drivers/media/pci/mantis/mantis_pcmcia.c | 4 +- kernel/drivers/media/pci/mantis/mantis_uart.c | 61 +- kernel/drivers/media/pci/mantis/mantis_vp1034.c | 2 +- kernel/drivers/media/pci/mantis/mantis_vp1034.h | 3 +- kernel/drivers/media/pci/netup_unidvb/Kconfig | 12 + kernel/drivers/media/pci/netup_unidvb/Makefile | 9 + .../drivers/media/pci/netup_unidvb/netup_unidvb.h | 133 + .../media/pci/netup_unidvb/netup_unidvb_ci.c | 248 ++ .../media/pci/netup_unidvb/netup_unidvb_core.c | 1002 +++++++ .../media/pci/netup_unidvb/netup_unidvb_i2c.c | 381 +++ .../media/pci/netup_unidvb/netup_unidvb_spi.c | 248 ++ kernel/drivers/media/pci/ngene/ngene-core.c | 10 +- kernel/drivers/media/pci/ngene/ngene.h | 2 +- kernel/drivers/media/pci/pt1/pt1.c | 6 +- kernel/drivers/media/pci/pt1/va1j5jf8007s.c | 4 +- kernel/drivers/media/pci/pt1/va1j5jf8007t.c | 4 +- kernel/drivers/media/pci/pt3/pt3.c | 2 +- kernel/drivers/media/pci/saa7134/saa7134-alsa.c | 60 +- kernel/drivers/media/pci/saa7134/saa7134-cards.c | 193 +- kernel/drivers/media/pci/saa7134/saa7134-core.c | 179 +- kernel/drivers/media/pci/saa7134/saa7134-dvb.c | 122 +- kernel/drivers/media/pci/saa7134/saa7134-empress.c | 55 +- kernel/drivers/media/pci/saa7134/saa7134-go7007.c | 11 +- kernel/drivers/media/pci/saa7134/saa7134-i2c.c | 87 +- kernel/drivers/media/pci/saa7134/saa7134-input.c | 66 +- kernel/drivers/media/pci/saa7134/saa7134-ts.c | 40 +- kernel/drivers/media/pci/saa7134/saa7134-tvaudio.c | 168 +- kernel/drivers/media/pci/saa7134/saa7134-vbi.c | 26 +- kernel/drivers/media/pci/saa7134/saa7134-video.c | 66 +- kernel/drivers/media/pci/saa7134/saa7134.h | 11 +- kernel/drivers/media/pci/saa7164/Kconfig | 1 - kernel/drivers/media/pci/saa7164/saa7164-api.c | 11 +- kernel/drivers/media/pci/saa7164/saa7164-buffer.c | 2 +- kernel/drivers/media/pci/saa7164/saa7164-bus.c | 2 +- kernel/drivers/media/pci/saa7164/saa7164-cards.c | 188 +- kernel/drivers/media/pci/saa7164/saa7164-cmd.c | 2 +- kernel/drivers/media/pci/saa7164/saa7164-core.c | 86 +- kernel/drivers/media/pci/saa7164/saa7164-dvb.c | 241 +- kernel/drivers/media/pci/saa7164/saa7164-encoder.c | 655 ++--- kernel/drivers/media/pci/saa7164/saa7164-fw.c | 2 +- kernel/drivers/media/pci/saa7164/saa7164-i2c.c | 9 +- kernel/drivers/media/pci/saa7164/saa7164-reg.h | 2 +- kernel/drivers/media/pci/saa7164/saa7164-types.h | 2 +- kernel/drivers/media/pci/saa7164/saa7164-vbi.c | 631 +---- kernel/drivers/media/pci/saa7164/saa7164.h | 34 +- kernel/drivers/media/pci/smipcie/Kconfig | 1 + kernel/drivers/media/pci/smipcie/Makefile | 3 + kernel/drivers/media/pci/smipcie/smipcie-ir.c | 232 ++ kernel/drivers/media/pci/smipcie/smipcie-main.c | 1114 ++++++++ kernel/drivers/media/pci/smipcie/smipcie.c | 1101 -------- kernel/drivers/media/pci/smipcie/smipcie.h | 19 + kernel/drivers/media/pci/solo6x10/solo6x10-core.c | 18 +- kernel/drivers/media/pci/solo6x10/solo6x10-g723.c | 13 +- .../drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c | 50 +- kernel/drivers/media/pci/solo6x10/solo6x10-v4l2.c | 23 +- kernel/drivers/media/pci/solo6x10/solo6x10.h | 30 +- kernel/drivers/media/pci/sta2x11/sta2x11_vip.c | 29 +- kernel/drivers/media/pci/ttpci/av7110.c | 27 +- kernel/drivers/media/pci/ttpci/av7110.h | 27 +- kernel/drivers/media/pci/ttpci/av7110_av.c | 6 +- kernel/drivers/media/pci/ttpci/budget-av.c | 2 +- kernel/drivers/media/pci/ttpci/budget-core.c | 3 +- kernel/drivers/media/pci/ttpci/budget-patch.c | 15 +- kernel/drivers/media/pci/ttpci/budget.c | 12 +- kernel/drivers/media/pci/ttpci/budget.h | 2 +- kernel/drivers/media/pci/ttpci/ttpci-eeprom.c | 9 +- kernel/drivers/media/pci/tw68/tw68-core.c | 25 +- kernel/drivers/media/pci/tw68/tw68-video.c | 24 +- kernel/drivers/media/pci/tw68/tw68.h | 19 +- kernel/drivers/media/pci/zoran/zoran.h | 7 +- kernel/drivers/media/pci/zoran/zoran_card.c | 11 +- kernel/drivers/media/pci/zoran/zoran_device.c | 31 +- kernel/drivers/media/pci/zoran/zoran_driver.c | 344 +-- kernel/drivers/media/platform/Kconfig | 39 +- kernel/drivers/media/platform/Makefile | 4 + kernel/drivers/media/platform/am437x/am437x-vpfe.c | 78 +- kernel/drivers/media/platform/am437x/am437x-vpfe.h | 3 +- .../drivers/media/platform/blackfin/bfin_capture.c | 77 +- kernel/drivers/media/platform/coda/Makefile | 2 +- kernel/drivers/media/platform/coda/coda-bit.c | 284 +- kernel/drivers/media/platform/coda/coda-common.c | 383 +-- kernel/drivers/media/platform/coda/coda-gdi.c | 150 ++ kernel/drivers/media/platform/coda/coda-jpeg.c | 6 +- kernel/drivers/media/platform/coda/coda.h | 24 +- kernel/drivers/media/platform/coda/coda_regs.h | 10 + kernel/drivers/media/platform/coda/trace.h | 103 +- .../drivers/media/platform/davinci/vpbe_display.c | 41 +- .../drivers/media/platform/davinci/vpfe_capture.c | 19 +- .../drivers/media/platform/davinci/vpif_capture.c | 33 +- .../drivers/media/platform/davinci/vpif_capture.h | 2 +- .../drivers/media/platform/davinci/vpif_display.c | 42 +- .../drivers/media/platform/davinci/vpif_display.h | 2 +- .../drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- .../drivers/media/platform/exynos-gsc/gsc-core.h | 4 +- kernel/drivers/media/platform/exynos-gsc/gsc-m2m.c | 25 +- kernel/drivers/media/platform/exynos4-is/Kconfig | 1 + .../media/platform/exynos4-is/fimc-capture.c | 33 +- .../drivers/media/platform/exynos4-is/fimc-core.c | 2 +- .../drivers/media/platform/exynos4-is/fimc-core.h | 4 +- kernel/drivers/media/platform/exynos4-is/fimc-is.h | 2 +- .../media/platform/exynos4-is/fimc-isp-video.c | 18 +- .../media/platform/exynos4-is/fimc-isp-video.h | 2 +- .../drivers/media/platform/exynos4-is/fimc-isp.h | 4 +- .../drivers/media/platform/exynos4-is/fimc-lite.c | 25 +- .../drivers/media/platform/exynos4-is/fimc-lite.h | 4 +- .../drivers/media/platform/exynos4-is/fimc-m2m.c | 25 +- .../drivers/media/platform/exynos4-is/media-dev.c | 2 +- .../drivers/media/platform/exynos4-is/mipi-csis.c | 3 +- kernel/drivers/media/platform/fsl-viu.c | 162 +- kernel/drivers/media/platform/m2m-deinterlace.c | 26 +- .../media/platform/marvell-ccic/cafe-driver.c | 13 +- .../media/platform/marvell-ccic/mcam-core.c | 518 ++-- .../media/platform/marvell-ccic/mcam-core.h | 5 +- .../media/platform/marvell-ccic/mmp-driver.c | 1 + kernel/drivers/media/platform/mx2_emmaprp.c | 17 +- kernel/drivers/media/platform/omap/Kconfig | 1 + kernel/drivers/media/platform/omap/omap_vout.c | 81 +- kernel/drivers/media/platform/omap3isp/isp.c | 140 +- kernel/drivers/media/platform/omap3isp/isp.h | 7 +- kernel/drivers/media/platform/omap3isp/ispcsiphy.h | 2 +- .../drivers/media/platform/omap3isp/isppreview.c | 4 - kernel/drivers/media/platform/omap3isp/ispstat.c | 5 +- kernel/drivers/media/platform/omap3isp/ispstat.h | 2 +- kernel/drivers/media/platform/omap3isp/ispvideo.c | 36 +- kernel/drivers/media/platform/omap3isp/ispvideo.h | 4 +- kernel/drivers/media/platform/omap3isp/omap3isp.h | 132 + kernel/drivers/media/platform/rcar_jpu.c | 1802 +++++++++++++ .../media/platform/s3c-camif/camif-capture.c | 39 +- .../drivers/media/platform/s3c-camif/camif-core.c | 4 +- .../drivers/media/platform/s3c-camif/camif-core.h | 4 +- kernel/drivers/media/platform/s5p-g2d/g2d.c | 21 +- kernel/drivers/media/platform/s5p-jpeg/jpeg-core.c | 489 +++- kernel/drivers/media/platform/s5p-jpeg/jpeg-core.h | 41 +- .../media/platform/s5p-jpeg/jpeg-hw-exynos4.c | 80 +- .../media/platform/s5p-jpeg/jpeg-hw-exynos4.h | 11 +- kernel/drivers/media/platform/s5p-jpeg/jpeg-regs.h | 85 +- kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c | 109 +- .../media/platform/s5p-mfc/s5p_mfc_cmd_v6.c | 6 +- .../media/platform/s5p-mfc/s5p_mfc_common.h | 4 +- .../drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 40 +- .../drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 82 +- .../drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 11 +- .../drivers/media/platform/s5p-mfc/s5p_mfc_opr.h | 2 +- .../media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 64 +- .../media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 82 +- kernel/drivers/media/platform/s5p-tv/hdmi_drv.c | 14 +- kernel/drivers/media/platform/s5p-tv/hdmiphy_drv.c | 1 - kernel/drivers/media/platform/s5p-tv/mixer.h | 4 +- kernel/drivers/media/platform/s5p-tv/mixer_drv.c | 15 +- .../media/platform/s5p-tv/mixer_grp_layer.c | 2 +- kernel/drivers/media/platform/s5p-tv/mixer_reg.c | 14 +- kernel/drivers/media/platform/s5p-tv/mixer_video.c | 13 +- .../drivers/media/platform/s5p-tv/mixer_vp_layer.c | 5 +- kernel/drivers/media/platform/s5p-tv/sdo_drv.c | 14 +- kernel/drivers/media/platform/s5p-tv/sii9234_drv.c | 1 - kernel/drivers/media/platform/sh_veu.c | 22 +- kernel/drivers/media/platform/sh_vou.c | 885 +++---- .../drivers/media/platform/soc_camera/atmel-isi.c | 318 +-- .../drivers/media/platform/soc_camera/atmel-isi.h | 128 + .../drivers/media/platform/soc_camera/mx2_camera.c | 137 +- .../drivers/media/platform/soc_camera/mx3_camera.c | 135 +- .../media/platform/soc_camera/omap1_camera.c | 106 +- .../drivers/media/platform/soc_camera/pxa_camera.c | 99 +- .../drivers/media/platform/soc_camera/rcar_vin.c | 191 +- .../platform/soc_camera/sh_mobile_ceu_camera.c | 181 +- .../media/platform/soc_camera/sh_mobile_csi2.c | 35 +- .../drivers/media/platform/soc_camera/soc_camera.c | 82 +- .../platform/soc_camera/soc_camera_platform.c | 24 +- .../media/platform/soc_camera/soc_scale_crop.c | 37 +- kernel/drivers/media/platform/sti/bdisp/Makefile | 3 + .../drivers/media/platform/sti/bdisp/bdisp-debug.c | 687 +++++ .../media/platform/sti/bdisp/bdisp-filter.h | 346 +++ kernel/drivers/media/platform/sti/bdisp/bdisp-hw.c | 823 ++++++ .../drivers/media/platform/sti/bdisp/bdisp-reg.h | 235 ++ .../drivers/media/platform/sti/bdisp/bdisp-v4l2.c | 1444 +++++++++++ kernel/drivers/media/platform/sti/bdisp/bdisp.h | 216 ++ .../drivers/media/platform/sti/c8sectpfe/Kconfig | 27 + .../drivers/media/platform/sti/c8sectpfe/Makefile | 9 + .../platform/sti/c8sectpfe/c8sectpfe-common.c | 265 ++ .../platform/sti/c8sectpfe/c8sectpfe-common.h | 64 + .../media/platform/sti/c8sectpfe/c8sectpfe-core.c | 1235 +++++++++ .../media/platform/sti/c8sectpfe/c8sectpfe-core.h | 288 +++ .../platform/sti/c8sectpfe/c8sectpfe-debugfs.c | 271 ++ .../platform/sti/c8sectpfe/c8sectpfe-debugfs.h | 26 + .../media/platform/sti/c8sectpfe/c8sectpfe-dvb.c | 244 ++ .../media/platform/sti/c8sectpfe/c8sectpfe-dvb.h | 20 + kernel/drivers/media/platform/ti-vpe/vpe.c | 44 +- kernel/drivers/media/platform/via-camera.c | 19 +- kernel/drivers/media/platform/vim2m.c | 69 +- kernel/drivers/media/platform/vivid/Kconfig | 8 + kernel/drivers/media/platform/vivid/vivid-core.c | 27 +- kernel/drivers/media/platform/vivid/vivid-core.h | 12 +- kernel/drivers/media/platform/vivid/vivid-ctrls.c | 164 +- .../media/platform/vivid/vivid-kthread-cap.c | 73 +- .../media/platform/vivid/vivid-kthread-out.c | 34 +- kernel/drivers/media/platform/vivid/vivid-osd.c | 1 + .../drivers/media/platform/vivid/vivid-radio-rx.c | 2 + .../drivers/media/platform/vivid/vivid-sdr-cap.c | 169 +- .../drivers/media/platform/vivid/vivid-sdr-cap.h | 2 + .../media/platform/vivid/vivid-tpg-colors.c | 720 +++++- .../media/platform/vivid/vivid-tpg-colors.h | 4 +- kernel/drivers/media/platform/vivid/vivid-tpg.c | 404 ++- kernel/drivers/media/platform/vivid/vivid-tpg.h | 20 + .../drivers/media/platform/vivid/vivid-vbi-cap.c | 40 +- .../drivers/media/platform/vivid/vivid-vbi-out.c | 20 +- .../drivers/media/platform/vivid/vivid-vid-cap.c | 51 +- .../media/platform/vivid/vivid-vid-common.c | 124 +- .../drivers/media/platform/vivid/vivid-vid-out.c | 40 +- kernel/drivers/media/platform/vsp1/vsp1_drv.c | 13 +- kernel/drivers/media/platform/vsp1/vsp1_entity.c | 18 +- kernel/drivers/media/platform/vsp1/vsp1_entity.h | 4 +- kernel/drivers/media/platform/vsp1/vsp1_regs.h | 6 +- kernel/drivers/media/platform/vsp1/vsp1_rpf.c | 4 +- kernel/drivers/media/platform/vsp1/vsp1_rwpf.c | 11 + kernel/drivers/media/platform/vsp1/vsp1_video.c | 108 +- kernel/drivers/media/platform/vsp1/vsp1_video.h | 13 +- kernel/drivers/media/platform/vsp1/vsp1_wpf.c | 4 +- kernel/drivers/media/platform/xilinx/Kconfig | 2 +- kernel/drivers/media/platform/xilinx/xilinx-dma.c | 37 +- kernel/drivers/media/platform/xilinx/xilinx-dma.h | 2 +- kernel/drivers/media/radio/radio-si476x.c | 4 +- kernel/drivers/media/radio/radio-tea5764.c | 1 - kernel/drivers/media/radio/radio-timb.c | 4 +- kernel/drivers/media/radio/saa7706h.c | 17 +- .../drivers/media/radio/si470x/radio-si470x-i2c.c | 9 +- .../drivers/media/radio/si470x/radio-si470x-usb.c | 6 +- kernel/drivers/media/radio/si470x/radio-si470x.h | 8 +- kernel/drivers/media/radio/si4713/si4713.c | 4 +- kernel/drivers/media/radio/tef6862.c | 1 - kernel/drivers/media/radio/wl128x/Kconfig | 4 +- kernel/drivers/media/radio/wl128x/fmdrv.h | 2 +- kernel/drivers/media/radio/wl128x/fmdrv_common.c | 5 +- kernel/drivers/media/rc/Kconfig | 26 +- kernel/drivers/media/rc/fintek-cir.c | 1 - kernel/drivers/media/rc/gpio-ir-recv.c | 4 +- kernel/drivers/media/rc/ir-hix5hd2.c | 10 +- kernel/drivers/media/rc/ir-lirc-codec.c | 5 +- kernel/drivers/media/rc/ir-sony-decoder.c | 28 +- kernel/drivers/media/rc/keymaps/Makefile | 4 + kernel/drivers/media/rc/keymaps/rc-lirc.c | 2 +- kernel/drivers/media/rc/keymaps/rc-lme2510.c | 132 +- .../drivers/media/rc/keymaps/rc-technisat-ts35.c | 76 + .../media/rc/keymaps/rc-terratec-cinergy-c-pci.c | 88 + .../media/rc/keymaps/rc-terratec-cinergy-s2-hd.c | 86 + .../media/rc/keymaps/rc-twinhan-dtv-cab-ci.c | 98 + kernel/drivers/media/rc/rc-ir-raw.c | 2 +- kernel/drivers/media/rc/rc-main.c | 73 +- kernel/drivers/media/rc/redrat3.c | 7 +- kernel/drivers/media/rc/st_rc.c | 12 +- kernel/drivers/media/rc/streamzap.c | 6 +- kernel/drivers/media/rc/sunxi-cir.c | 2 + kernel/drivers/media/tuners/Kconfig | 7 +- kernel/drivers/media/tuners/e4000.c | 593 +++-- kernel/drivers/media/tuners/e4000.h | 1 - kernel/drivers/media/tuners/e4000_priv.h | 11 +- kernel/drivers/media/tuners/fc0013.c | 2 - kernel/drivers/media/tuners/fc2580.c | 780 +++--- kernel/drivers/media/tuners/fc2580.h | 40 +- kernel/drivers/media/tuners/fc2580_priv.h | 36 +- kernel/drivers/media/tuners/it913x.c | 1 - kernel/drivers/media/tuners/m88rs6000t.c | 1 - kernel/drivers/media/tuners/msi001.c | 268 +- kernel/drivers/media/tuners/qt1010.c | 8 +- kernel/drivers/media/tuners/r820t.c | 4 +- kernel/drivers/media/tuners/si2157.c | 50 +- kernel/drivers/media/tuners/si2157.h | 6 + kernel/drivers/media/tuners/si2157_priv.h | 2 + kernel/drivers/media/tuners/tda18212.c | 1 - kernel/drivers/media/tuners/tua9001.c | 330 ++- kernel/drivers/media/tuners/tua9001.h | 35 +- kernel/drivers/media/tuners/tua9001_priv.h | 19 +- kernel/drivers/media/tuners/tuner-i2c.h | 10 +- kernel/drivers/media/tuners/tuner-xc2028.c | 2 +- kernel/drivers/media/usb/airspy/airspy.c | 31 +- kernel/drivers/media/usb/as102/as102_drv.c | 1 + kernel/drivers/media/usb/au0828/au0828-cards.c | 2 - kernel/drivers/media/usb/au0828/au0828-core.c | 2 + kernel/drivers/media/usb/au0828/au0828-vbi.c | 10 +- kernel/drivers/media/usb/au0828/au0828-video.c | 48 +- kernel/drivers/media/usb/au0828/au0828.h | 3 +- kernel/drivers/media/usb/cx231xx/cx231xx-417.c | 21 +- kernel/drivers/media/usb/cx231xx/cx231xx-avcore.c | 44 +- kernel/drivers/media/usb/cx231xx/cx231xx-cards.c | 56 +- kernel/drivers/media/usb/cx231xx/cx231xx-core.c | 30 +- kernel/drivers/media/usb/cx231xx/cx231xx-dvb.c | 2 + kernel/drivers/media/usb/cx231xx/cx231xx-vbi.c | 3 +- kernel/drivers/media/usb/cx231xx/cx231xx-video.c | 33 +- kernel/drivers/media/usb/cx231xx/cx231xx.h | 1 + kernel/drivers/media/usb/dvb-usb-v2/af9015.c | 2 +- kernel/drivers/media/usb/dvb-usb-v2/af9015.h | 2 +- kernel/drivers/media/usb/dvb-usb-v2/af9035.c | 58 +- kernel/drivers/media/usb/dvb-usb-v2/dvbsky.c | 22 +- kernel/drivers/media/usb/dvb-usb-v2/lmedm04.c | 133 +- .../drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c | 14 +- kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 210 +- kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 7 +- kernel/drivers/media/usb/dvb-usb/af9005-fe.c | 7 +- kernel/drivers/media/usb/dvb-usb/az6027.c | 3 +- kernel/drivers/media/usb/dvb-usb/cinergyT2-fe.c | 2 +- kernel/drivers/media/usb/dvb-usb/cxusb.c | 1 + kernel/drivers/media/usb/dvb-usb/dib0700.h | 2 +- kernel/drivers/media/usb/dvb-usb/dib0700_devices.c | 151 +- kernel/drivers/media/usb/dvb-usb/dtt200u-fe.c | 7 +- kernel/drivers/media/usb/dvb-usb/dw2102.c | 55 +- kernel/drivers/media/usb/dvb-usb/friio-fe.c | 3 +- kernel/drivers/media/usb/dvb-usb/gp8psk-fe.c | 13 +- kernel/drivers/media/usb/dvb-usb/opera1.c | 3 +- kernel/drivers/media/usb/dvb-usb/pctv452e.c | 2 +- kernel/drivers/media/usb/dvb-usb/technisat-usb2.c | 4 +- kernel/drivers/media/usb/dvb-usb/vp702x-fe.c | 17 +- kernel/drivers/media/usb/dvb-usb/vp702x.c | 7 +- kernel/drivers/media/usb/dvb-usb/vp7045-fe.c | 3 +- kernel/drivers/media/usb/em28xx/em28xx-camera.c | 12 +- kernel/drivers/media/usb/em28xx/em28xx-dvb.c | 220 +- kernel/drivers/media/usb/em28xx/em28xx-vbi.c | 10 +- kernel/drivers/media/usb/em28xx/em28xx-video.c | 38 +- kernel/drivers/media/usb/em28xx/em28xx.h | 3 +- kernel/drivers/media/usb/go7007/go7007-driver.c | 30 +- kernel/drivers/media/usb/go7007/go7007-fw.c | 6 +- kernel/drivers/media/usb/go7007/go7007-priv.h | 4 +- kernel/drivers/media/usb/go7007/go7007-usb.c | 4 + kernel/drivers/media/usb/go7007/go7007-v4l2.c | 34 +- kernel/drivers/media/usb/go7007/s2250-board.c | 19 +- kernel/drivers/media/usb/gspca/benq.c | 4 +- kernel/drivers/media/usb/gspca/gspca.c | 4 +- .../drivers/media/usb/gspca/m5602/m5602_s5k83a.c | 2 +- kernel/drivers/media/usb/gspca/ov534.c | 9 +- kernel/drivers/media/usb/gspca/sn9c2028.c | 243 +- kernel/drivers/media/usb/gspca/sn9c2028.h | 18 +- kernel/drivers/media/usb/gspca/sonixj.c | 2 +- kernel/drivers/media/usb/gspca/stk014.c | 2 +- kernel/drivers/media/usb/gspca/topro.c | 6 +- kernel/drivers/media/usb/gspca/xirlink_cit.c | 12 +- kernel/drivers/media/usb/gspca/zc3xx.c | 16 +- kernel/drivers/media/usb/hackrf/hackrf.c | 1097 +++++--- kernel/drivers/media/usb/msi2500/msi2500.c | 670 ++--- kernel/drivers/media/usb/pvrusb2/pvrusb2-context.c | 3 +- kernel/drivers/media/usb/pvrusb2/pvrusb2-hdw.c | 35 +- kernel/drivers/media/usb/pvrusb2/pvrusb2-io.c | 30 +- kernel/drivers/media/usb/pvrusb2/pvrusb2-ioread.c | 24 +- kernel/drivers/media/usb/pwc/pwc-if.c | 35 +- kernel/drivers/media/usb/pwc/pwc-uncompress.c | 6 +- kernel/drivers/media/usb/pwc/pwc.h | 4 +- kernel/drivers/media/usb/s2255/s2255drv.c | 29 +- kernel/drivers/media/usb/stk1160/stk1160-core.c | 5 +- kernel/drivers/media/usb/stk1160/stk1160-reg.h | 34 + kernel/drivers/media/usb/stk1160/stk1160-v4l.c | 235 +- kernel/drivers/media/usb/stk1160/stk1160-video.c | 14 +- kernel/drivers/media/usb/stk1160/stk1160.h | 8 +- kernel/drivers/media/usb/tm6000/tm6000-alsa.c | 2 +- kernel/drivers/media/usb/tm6000/tm6000-video.c | 5 +- .../media/usb/ttusb-budget/dvb-ttusb-budget.c | 9 +- kernel/drivers/media/usb/ttusb-dec/ttusb_dec.c | 25 +- kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.c | 10 +- kernel/drivers/media/usb/usbtv/usbtv-video.c | 34 +- kernel/drivers/media/usb/usbtv/usbtv.h | 3 +- .../drivers/media/usb/usbvision/usbvision-core.c | 75 +- kernel/drivers/media/usb/usbvision/usbvision-i2c.c | 2 +- .../drivers/media/usb/usbvision/usbvision-video.c | 263 +- kernel/drivers/media/usb/usbvision/usbvision.h | 10 +- kernel/drivers/media/usb/uvc/uvc_driver.c | 5 +- kernel/drivers/media/usb/uvc/uvc_queue.c | 41 +- kernel/drivers/media/usb/uvc/uvc_v4l2.c | 16 +- kernel/drivers/media/usb/uvc/uvc_video.c | 31 +- kernel/drivers/media/usb/uvc/uvcvideo.h | 14 +- kernel/drivers/media/usb/zr364xx/zr364xx.c | 3 +- kernel/drivers/media/v4l2-core/Kconfig | 14 +- kernel/drivers/media/v4l2-core/Makefile | 7 +- kernel/drivers/media/v4l2-core/tuner-core.c | 1 - kernel/drivers/media/v4l2-core/v4l2-async.c | 39 +- .../drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 51 +- kernel/drivers/media/v4l2-core/v4l2-ctrls.c | 29 +- kernel/drivers/media/v4l2-core/v4l2-dev.c | 14 +- kernel/drivers/media/v4l2-core/v4l2-dv-timings.c | 211 +- kernel/drivers/media/v4l2-core/v4l2-event.c | 3 + .../drivers/media/v4l2-core/v4l2-flash-led-class.c | 710 +++++ kernel/drivers/media/v4l2-core/v4l2-ioctl.c | 271 +- kernel/drivers/media/v4l2-core/v4l2-mem2mem.c | 86 +- kernel/drivers/media/v4l2-core/v4l2-of.c | 100 +- kernel/drivers/media/v4l2-core/v4l2-subdev.c | 18 + kernel/drivers/media/v4l2-core/v4l2-trace.c | 11 + kernel/drivers/media/v4l2-core/vb2-trace.c | 9 + kernel/drivers/media/v4l2-core/videobuf-core.c | 4 +- kernel/drivers/media/v4l2-core/videobuf2-core.c | 2059 +++------------ .../drivers/media/v4l2-core/videobuf2-dma-contig.c | 220 +- kernel/drivers/media/v4l2-core/videobuf2-dma-sg.c | 120 +- .../drivers/media/v4l2-core/videobuf2-internal.h | 161 ++ kernel/drivers/media/v4l2-core/videobuf2-memops.c | 150 +- kernel/drivers/media/v4l2-core/videobuf2-v4l2.c | 1661 ++++++++++++ kernel/drivers/media/v4l2-core/videobuf2-vmalloc.c | 98 +- 706 files changed, 48395 insertions(+), 16203 deletions(-) create mode 100644 kernel/drivers/media/dvb-frontends/ascot2e.c create mode 100644 kernel/drivers/media/dvb-frontends/ascot2e.h create mode 100644 kernel/drivers/media/dvb-frontends/cx24120.c create mode 100644 kernel/drivers/media/dvb-frontends/cx24120.h create mode 100644 kernel/drivers/media/dvb-frontends/cxd2841er.c create mode 100644 kernel/drivers/media/dvb-frontends/cxd2841er.h create mode 100644 kernel/drivers/media/dvb-frontends/cxd2841er_priv.h create mode 100644 kernel/drivers/media/dvb-frontends/horus3a.c create mode 100644 kernel/drivers/media/dvb-frontends/horus3a.h create mode 100644 kernel/drivers/media/dvb-frontends/lnbh25.c create mode 100644 kernel/drivers/media/dvb-frontends/lnbh25.h create mode 100644 kernel/drivers/media/i2c/tc358743.c create mode 100644 kernel/drivers/media/i2c/tc358743_regs.h create mode 100644 kernel/drivers/media/pci/cobalt/Kconfig create mode 100644 kernel/drivers/media/pci/cobalt/Makefile create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-alsa-main.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-alsa.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-cpld.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-cpld.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-driver.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-driver.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-flash.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-flash.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-i2c.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-i2c.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-irq.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-irq.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-omnitek.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-omnitek.h create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-v4l2.c create mode 100644 kernel/drivers/media/pci/cobalt/cobalt-v4l2.h create mode 100644 kernel/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h create mode 100644 kernel/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h create mode 100644 kernel/drivers/media/pci/dt3155/Kconfig create mode 100644 kernel/drivers/media/pci/dt3155/Makefile create mode 100644 kernel/drivers/media/pci/dt3155/dt3155.c create mode 100644 kernel/drivers/media/pci/dt3155/dt3155.h create mode 100644 kernel/drivers/media/pci/mantis/mantis_input.h create mode 100644 kernel/drivers/media/pci/netup_unidvb/Kconfig create mode 100644 kernel/drivers/media/pci/netup_unidvb/Makefile create mode 100644 kernel/drivers/media/pci/netup_unidvb/netup_unidvb.h create mode 100644 kernel/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c create mode 100644 kernel/drivers/media/pci/netup_unidvb/netup_unidvb_core.c create mode 100644 kernel/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c create mode 100644 kernel/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c create mode 100644 kernel/drivers/media/pci/smipcie/smipcie-ir.c create mode 100644 kernel/drivers/media/pci/smipcie/smipcie-main.c delete mode 100644 kernel/drivers/media/pci/smipcie/smipcie.c create mode 100644 kernel/drivers/media/platform/coda/coda-gdi.c create mode 100644 kernel/drivers/media/platform/omap3isp/omap3isp.h create mode 100644 kernel/drivers/media/platform/rcar_jpu.c create mode 100644 kernel/drivers/media/platform/soc_camera/atmel-isi.h create mode 100644 kernel/drivers/media/platform/sti/bdisp/Makefile create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp-debug.c create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp-filter.h create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp-hw.c create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp-reg.h create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp-v4l2.c create mode 100644 kernel/drivers/media/platform/sti/bdisp/bdisp.h create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/Kconfig create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/Makefile create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c create mode 100644 kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h create mode 100644 kernel/drivers/media/rc/keymaps/rc-technisat-ts35.c create mode 100644 kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c create mode 100644 kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c create mode 100644 kernel/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c create mode 100644 kernel/drivers/media/v4l2-core/v4l2-flash-led-class.c create mode 100644 kernel/drivers/media/v4l2-core/v4l2-trace.c create mode 100644 kernel/drivers/media/v4l2-core/vb2-trace.c create mode 100644 kernel/drivers/media/v4l2-core/videobuf2-internal.h create mode 100644 kernel/drivers/media/v4l2-core/videobuf2-v4l2.c (limited to 'kernel/drivers/media') diff --git a/kernel/drivers/media/Kconfig b/kernel/drivers/media/Kconfig index 157099243..3ef3d6c6b 100644 --- a/kernel/drivers/media/Kconfig +++ b/kernel/drivers/media/Kconfig @@ -95,7 +95,7 @@ config MEDIA_CONTROLLER This API is mostly used by camera interfaces in embedded platforms. config MEDIA_CONTROLLER_DVB - bool "Enable Media controller for DVB" + bool "Enable Media controller for DVB (EXPERIMENTAL)" depends on MEDIA_CONTROLLER depends on BROKEN ---help--- diff --git a/kernel/drivers/media/common/b2c2/Kconfig b/kernel/drivers/media/common/b2c2/Kconfig index a8c6cdfaa..e5936380b 100644 --- a/kernel/drivers/media/common/b2c2/Kconfig +++ b/kernel/drivers/media/common/b2c2/Kconfig @@ -14,6 +14,7 @@ config DVB_B2C2_FLEXCOP select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24120 if MEDIA_SUBDRV_AUTOSELECT select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT diff --git a/kernel/drivers/media/common/b2c2/flexcop-common.h b/kernel/drivers/media/common/b2c2/flexcop-common.h index 437912e49..2b2460e9e 100644 --- a/kernel/drivers/media/common/b2c2/flexcop-common.h +++ b/kernel/drivers/media/common/b2c2/flexcop-common.h @@ -91,6 +91,7 @@ struct flexcop_device { int feedcount; int pid_filtering; int fullts_streaming_state; + int skip_6_hw_pid_filter; /* bus specific callbacks */ flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, diff --git a/kernel/drivers/media/common/b2c2/flexcop-fe-tuner.c b/kernel/drivers/media/common/b2c2/flexcop-fe-tuner.c index 7e14e90d2..9c59f4306 100644 --- a/kernel/drivers/media/common/b2c2/flexcop-fe-tuner.c +++ b/kernel/drivers/media/common/b2c2/flexcop-fe-tuner.c @@ -12,6 +12,7 @@ #include "cx24113.h" #include "cx24123.h" #include "isl6421.h" +#include "cx24120.h" #include "mt352.h" #include "bcm3510.h" #include "nxt200x.h" @@ -26,9 +27,20 @@ #define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) +#if FE_SUPPORTED(BCM3510) || (FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421)) +static int flexcop_fe_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct flexcop_device *fc = fe->dvb->priv; + + return request_firmware(fw, name, fc->dev); +} +#endif + /* lnb control */ #if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) -static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int flexcop_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct flexcop_device *fc = fe->dvb->priv; flexcop_ibi_value v; @@ -67,7 +79,7 @@ static int flexcop_sleep(struct dvb_frontend* fe) /* SkyStar2 DVB-S rev 2.3 */ #if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL) -static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int flexcop_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */ struct flexcop_device *fc = fe->dvb->priv; @@ -146,7 +158,7 @@ static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe, } static int flexcop_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) + enum fe_sec_mini_cmd minicmd) { return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd); } @@ -445,13 +457,6 @@ static int airstar_dvbt_attach(struct flexcop_device *fc, /* AirStar ATSC 1st generation */ #if FE_SUPPORTED(BCM3510) -static int flexcop_fe_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char* name) -{ - struct flexcop_device *fc = fe->dvb->priv; - return request_firmware(fw, name, fc->dev); -} - static struct bcm3510_config air2pc_atsc_first_gen_config = { .demod_address = 0x0f, .request_firmware = flexcop_fe_request_firmware, @@ -619,6 +624,43 @@ fail: #define cablestar2_attach NULL #endif +/* SkyStar S2 PCI DVB-S/S2 card based on Conexant cx24120/cx24118 */ +#if FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421) +static const struct cx24120_config skystar2_rev3_3_cx24120_config = { + .i2c_addr = 0x55, + .xtal_khz = 10111, + .initial_mpeg_config = { 0xa1, 0x76, 0x07 }, + .request_firmware = flexcop_fe_request_firmware, + .i2c_wr_max = 4, +}; + +static int skystarS2_rev33_attach(struct flexcop_device *fc, + struct i2c_adapter *i2c) +{ + fc->fe = dvb_attach(cx24120_attach, + &skystar2_rev3_3_cx24120_config, i2c); + if (!fc->fe) + return 0; + + fc->dev_type = FC_SKYS2_REV33; + fc->fc_i2c_adap[2].no_base_addr = 1; + if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap, + 0x08, 0, 0, false)) { + err("ISL6421 could NOT be attached!"); + fc->fc_i2c_adap[2].no_base_addr = 0; + return 0; + } + info("ISL6421 successfully attached."); + + if (fc->has_32_hw_pid_filter) + fc->skip_6_hw_pid_filter = 1; + + return 1; +} +#else +#define skystarS2_rev33_attach NULL +#endif + static struct { flexcop_device_type_t type; int (*attach)(struct flexcop_device *, struct i2c_adapter *); @@ -632,6 +674,7 @@ static struct { { FC_AIR_ATSC1, airstar_atsc1_attach }, { FC_CABLE, cablestar2_attach }, { FC_SKY_REV23, skystar2_rev23_attach }, + { FC_SKYS2_REV33, skystarS2_rev33_attach }, }; /* try to figure out the frontend */ diff --git a/kernel/drivers/media/common/b2c2/flexcop-hw-filter.c b/kernel/drivers/media/common/b2c2/flexcop-hw-filter.c index 77e45475f..822025790 100644 --- a/kernel/drivers/media/common/b2c2/flexcop-hw-filter.c +++ b/kernel/drivers/media/common/b2c2/flexcop-hw-filter.c @@ -117,6 +117,10 @@ static void flexcop_pid_control(struct flexcop_device *fc, deb_ts("setting pid: %5d %04x at index %d '%s'\n", pid, pid, index, onoff ? "on" : "off"); + /* First 6 can be buggy - skip over them if option set */ + if (fc->skip_6_hw_pid_filter) + index += 6; + /* We could use bit magic here to reduce source code size. * I decided against it, but to use the real register names */ switch (index) { @@ -170,7 +174,10 @@ static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff) { - int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32; + int max_pid_filter = 6; + + max_pid_filter -= 6 * fc->skip_6_hw_pid_filter; + max_pid_filter += 32 * fc->has_32_hw_pid_filter; fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ if (dvbdmxfeed->index >= max_pid_filter) @@ -217,7 +224,12 @@ void flexcop_hw_filter_init(struct flexcop_device *fc) { int i; flexcop_ibi_value v; - for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++) + int max_pid_filter = 6; + + max_pid_filter -= 6 * fc->skip_6_hw_pid_filter; + max_pid_filter += 32 * fc->has_32_hw_pid_filter; + + for (i = 0; i < max_pid_filter; i++) flexcop_pid_control(fc, i, 0x1fff, 0); flexcop_pid_group_filter(fc, 0, 0x1fe0); diff --git a/kernel/drivers/media/common/b2c2/flexcop-misc.c b/kernel/drivers/media/common/b2c2/flexcop-misc.c index f06f3a907..b8eff2353 100644 --- a/kernel/drivers/media/common/b2c2/flexcop-misc.c +++ b/kernel/drivers/media/common/b2c2/flexcop-misc.c @@ -56,6 +56,7 @@ static const char *flexcop_device_names[] = { [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", + [FC_SKYS2_REV33] = "Sky2PC/SkyStar S2 DVB-S/S2 rev 3.3", }; static const char *flexcop_bus_names[] = { diff --git a/kernel/drivers/media/common/b2c2/flexcop-reg.h b/kernel/drivers/media/common/b2c2/flexcop-reg.h index dc4528dcb..835c54d60 100644 --- a/kernel/drivers/media/common/b2c2/flexcop-reg.h +++ b/kernel/drivers/media/common/b2c2/flexcop-reg.h @@ -24,6 +24,7 @@ typedef enum { FC_SKY_REV26, FC_SKY_REV27, FC_SKY_REV28, + FC_SKYS2_REV33, } flexcop_device_type_t; typedef enum { diff --git a/kernel/drivers/media/common/saa7146/saa7146_hlp.c b/kernel/drivers/media/common/saa7146/saa7146_hlp.c index be746d1ae..3dc6a838c 100644 --- a/kernel/drivers/media/common/saa7146/saa7146_hlp.c +++ b/kernel/drivers/media/common/saa7146/saa7146_hlp.c @@ -307,7 +307,7 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field /* simple bubble-sort algorithm with duplicate elimination */ static int sort_and_eliminate(u32* values, int* count) { - int low = 0, high = 0, top = 0, temp = 0; + int low = 0, high = 0, top = 0; int cur = 0, next = 0; /* sanity checks */ @@ -318,11 +318,8 @@ static int sort_and_eliminate(u32* values, int* count) /* bubble sort the first @count items of the array @values */ for( top = *count; top > 0; top--) { for( low = 0, high = 1; high < top; low++, high++) { - if( values[low] > values[high] ) { - temp = values[low]; - values[low] = values[high]; - values[high] = temp; - } + if( values[low] > values[high] ) + swap(values[low], values[high]); } } diff --git a/kernel/drivers/media/common/siano/smscoreapi.h b/kernel/drivers/media/common/siano/smscoreapi.h index eb8bd689b..4cc39e4a8 100644 --- a/kernel/drivers/media/common/siano/smscoreapi.h +++ b/kernel/drivers/media/common/siano/smscoreapi.h @@ -1010,6 +1010,7 @@ struct sms_rx_stats_ex { s32 mrc_in_band_pwr; /* In band power in dBM */ }; +#define SRVM_MAX_PID_FILTERS 8 /* statistics information returned as response for * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */ @@ -1021,7 +1022,6 @@ struct sms_stats_dvb { struct sms_tx_stats transmission_data; /* Burst parameters, valid only for DVB-H */ -#define SRVM_MAX_PID_FILTERS 8 struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS]; }; @@ -1035,7 +1035,6 @@ struct sms_stats_dvb_ex { struct sms_tx_stats transmission_data; /* Burst parameters, valid only for DVB-H */ -#define SRVM_MAX_PID_FILTERS 8 struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS]; }; diff --git a/kernel/drivers/media/common/siano/smsdvb-main.c b/kernel/drivers/media/common/siano/smsdvb-main.c index 367b8e77f..f4305ae80 100644 --- a/kernel/drivers/media/common/siano/smsdvb-main.c +++ b/kernel/drivers/media/common/siano/smsdvb-main.c @@ -753,7 +753,7 @@ static inline int led_feedback(struct smsdvb_client_t *client) SMS_LED_HI : SMS_LED_LO); } -static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) +static int smsdvb_read_status(struct dvb_frontend *fe, enum fe_status *stat) { int rc; struct smsdvb_client_t *client; @@ -900,7 +900,7 @@ static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe) /* Disable LNA, if any. An error is returned if no LNA is present */ ret = sms_board_lna_control(client->coredev, 0); if (ret == 0) { - fe_status_t status; + enum fe_status status; /* tune with LNA off at first */ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), @@ -971,7 +971,7 @@ static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe) /* Disable LNA, if any. An error is returned if no LNA is present */ ret = sms_board_lna_control(client->coredev, 0); if (ret == 0) { - fe_status_t status; + enum fe_status status; /* tune with LNA off at first */ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg), diff --git a/kernel/drivers/media/common/siano/smsdvb.h b/kernel/drivers/media/common/siano/smsdvb.h index ae36d0ae0..b15754d95 100644 --- a/kernel/drivers/media/common/siano/smsdvb.h +++ b/kernel/drivers/media/common/siano/smsdvb.h @@ -40,7 +40,7 @@ struct smsdvb_client_t { struct dmxdev dmxdev; struct dvb_frontend frontend; - fe_status_t fe_status; + enum fe_status fe_status; struct completion tune_done; struct completion stats_done; diff --git a/kernel/drivers/media/common/siano/smsir.c b/kernel/drivers/media/common/siano/smsir.c index 1d60d200d..41f2a3939 100644 --- a/kernel/drivers/media/common/siano/smsir.c +++ b/kernel/drivers/media/common/siano/smsir.c @@ -78,7 +78,7 @@ int sms_ir_init(struct smscore_device_t *coredev) dev->dev.parent = coredev->device; #if 0 - /* TODO: properly initialize the parameters bellow */ + /* TODO: properly initialize the parameters below */ dev->input_id.bustype = BUS_USB; dev->input_id.version = 1; dev->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); diff --git a/kernel/drivers/media/dvb-core/demux.h b/kernel/drivers/media/dvb-core/demux.h index 833191bcd..ccc1f43cb 100644 --- a/kernel/drivers/media/dvb-core/demux.h +++ b/kernel/drivers/media/dvb-core/demux.h @@ -32,9 +32,9 @@ #include #include -/*--------------------------------------------------------------------------*/ -/* Common definitions */ -/*--------------------------------------------------------------------------*/ +/* + * Common definitions + */ /* * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter. @@ -45,7 +45,8 @@ #endif /* - * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter. + * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed + * filter. */ #ifndef DMX_MAX_SECTION_SIZE @@ -55,139 +56,296 @@ #define DMX_MAX_SECFEED_SIZE (DMX_MAX_SECTION_SIZE + 188) #endif - /* - * enum dmx_success: Success codes for the Demux Callback API. + * TS packet reception */ -enum dmx_success { - DMX_OK = 0, /* Received Ok */ - DMX_LENGTH_ERROR, /* Incorrect length */ - DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */ - DMX_CRC_ERROR, /* Incorrect CRC */ - DMX_FRAME_ERROR, /* Frame alignment error */ - DMX_FIFO_ERROR, /* Receiver FIFO overrun */ - DMX_MISSED_ERROR /* Receiver missed packet */ -} ; - -/*--------------------------------------------------------------------------*/ -/* TS packet reception */ -/*--------------------------------------------------------------------------*/ - -/* TS filter type for set() */ - -#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */ -#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS - payload (<=184 bytes per packet) to callback */ -#define TS_DECODER 4 /* send stream to built-in decoder (if present) */ -#define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to - the demux device, not to the dvr device */ +/** + * enum ts_filter_type - filter type bitmap for dmx_ts_feed.set() + * + * @TS_PACKET: Send TS packets (188 bytes) to callback (default). + * @TS_PAYLOAD_ONLY: In case TS_PACKET is set, only send the TS payload + * (<=184 bytes per packet) to callback + * @TS_DECODER: Send stream to built-in decoder (if present). + * @TS_DEMUX: In case TS_PACKET is set, send the TS to the demux + * device, not to the dvr device + */ +enum ts_filter_type { + TS_PACKET = 1, + TS_PAYLOAD_ONLY = 2, + TS_DECODER = 4, + TS_DEMUX = 8, +}; +/** + * struct dmx_ts_feed - Structure that contains a TS feed filter + * + * @is_filtering: Set to non-zero when filtering in progress + * @parent: pointer to struct dmx_demux + * @priv: pointer to private data of the API client + * @set: sets the TS filter + * @start_filtering: starts TS filtering + * @stop_filtering: stops TS filtering + * + * A TS feed is typically mapped to a hardware PID filter on the demux chip. + * Using this API, the client can set the filtering properties to start/stop + * filtering TS packets on a particular TS feed. + */ struct dmx_ts_feed { - int is_filtering; /* Set to non-zero when filtering in progress */ - struct dmx_demux *parent; /* Back-pointer */ - void *priv; /* Pointer to private data of the API client */ - int (*set) (struct dmx_ts_feed *feed, - u16 pid, - int type, - enum dmx_ts_pes pes_type, - size_t circular_buffer_size, - struct timespec timeout); - int (*start_filtering) (struct dmx_ts_feed* feed); - int (*stop_filtering) (struct dmx_ts_feed* feed); + int is_filtering; + struct dmx_demux *parent; + void *priv; + int (*set)(struct dmx_ts_feed *feed, + u16 pid, + int type, + enum dmx_ts_pes pes_type, + size_t circular_buffer_size, + struct timespec timeout); + int (*start_filtering)(struct dmx_ts_feed *feed); + int (*stop_filtering)(struct dmx_ts_feed *feed); }; -/*--------------------------------------------------------------------------*/ -/* Section reception */ -/*--------------------------------------------------------------------------*/ +/* + * Section reception + */ +/** + * struct dmx_section_filter - Structure that describes a section filter + * + * @filter_value: Contains up to 16 bytes (128 bits) of the TS section header + * that will be matched by the section filter + * @filter_mask: Contains a 16 bytes (128 bits) filter mask with the bits + * specified by @filter_value that will be used on the filter + * match logic. + * @filter_mode: Contains a 16 bytes (128 bits) filter mode. + * @parent: Pointer to struct dmx_section_feed. + * @priv: Pointer to private data of the API client. + * + * + * The @filter_mask controls which bits of @filter_value are compared with + * the section headers/payload. On a binary value of 1 in filter_mask, the + * corresponding bits are compared. The filter only accepts sections that are + * equal to filter_value in all the tested bit positions. + */ struct dmx_section_filter { - u8 filter_value [DMX_MAX_FILTER_SIZE]; - u8 filter_mask [DMX_MAX_FILTER_SIZE]; - u8 filter_mode [DMX_MAX_FILTER_SIZE]; - struct dmx_section_feed* parent; /* Back-pointer */ - void* priv; /* Pointer to private data of the API client */ + u8 filter_value[DMX_MAX_FILTER_SIZE]; + u8 filter_mask[DMX_MAX_FILTER_SIZE]; + u8 filter_mode[DMX_MAX_FILTER_SIZE]; + struct dmx_section_feed *parent; /* Back-pointer */ + void *priv; /* Pointer to private data of the API client */ }; +/** + * struct dmx_section_feed - Structure that contains a section feed filter + * + * @is_filtering: Set to non-zero when filtering in progress + * @parent: pointer to struct dmx_demux + * @priv: pointer to private data of the API client + * @check_crc: If non-zero, check the CRC values of filtered sections. + * @set: sets the section filter + * @allocate_filter: This function is used to allocate a section filter on + * the demux. It should only be called when no filtering + * is in progress on this section feed. If a filter cannot + * be allocated, the function fails with -ENOSPC. + * @release_filter: This function releases all the resources of a + * previously allocated section filter. The function + * should not be called while filtering is in progress + * on this section feed. After calling this function, + * the caller should not try to dereference the filter + * pointer. + * @start_filtering: starts section filtering + * @stop_filtering: stops section filtering + * + * A TS feed is typically mapped to a hardware PID filter on the demux chip. + * Using this API, the client can set the filtering properties to start/stop + * filtering TS packets on a particular TS feed. + */ struct dmx_section_feed { - int is_filtering; /* Set to non-zero when filtering in progress */ - struct dmx_demux* parent; /* Back-pointer */ - void* priv; /* Pointer to private data of the API client */ + int is_filtering; + struct dmx_demux *parent; + void *priv; int check_crc; + + /* private: Used internally at dvb_demux.c */ u32 crc_val; u8 *secbuf; u8 secbuf_base[DMX_MAX_SECFEED_SIZE]; u16 secbufp, seclen, tsfeedp; - int (*set) (struct dmx_section_feed* feed, - u16 pid, - size_t circular_buffer_size, - int check_crc); - int (*allocate_filter) (struct dmx_section_feed* feed, - struct dmx_section_filter** filter); - int (*release_filter) (struct dmx_section_feed* feed, - struct dmx_section_filter* filter); - int (*start_filtering) (struct dmx_section_feed* feed); - int (*stop_filtering) (struct dmx_section_feed* feed); + /* public: */ + int (*set)(struct dmx_section_feed *feed, + u16 pid, + size_t circular_buffer_size, + int check_crc); + int (*allocate_filter)(struct dmx_section_feed *feed, + struct dmx_section_filter **filter); + int (*release_filter)(struct dmx_section_feed *feed, + struct dmx_section_filter *filter); + int (*start_filtering)(struct dmx_section_feed *feed); + int (*stop_filtering)(struct dmx_section_feed *feed); }; -/*--------------------------------------------------------------------------*/ -/* Callback functions */ -/*--------------------------------------------------------------------------*/ - -typedef int (*dmx_ts_cb) ( const u8 * buffer1, - size_t buffer1_length, - const u8 * buffer2, - size_t buffer2_length, - struct dmx_ts_feed* source, - enum dmx_success success); +/* + * Callback functions + */ -typedef int (*dmx_section_cb) ( const u8 * buffer1, - size_t buffer1_len, - const u8 * buffer2, - size_t buffer2_len, - struct dmx_section_filter * source, - enum dmx_success success); +/** + * typedef dmx_ts_cb - DVB demux TS filter callback function prototype + * + * @buffer1: Pointer to the start of the filtered TS packets. + * @buffer1_length: Length of the TS data in buffer1. + * @buffer2: Pointer to the tail of the filtered TS packets, or NULL. + * @buffer2_length: Length of the TS data in buffer2. + * @source: Indicates which TS feed is the source of the callback. + * + * This function callback prototype, provided by the client of the demux API, + * is called from the demux code. The function is only called when filtering + * on ae TS feed has been enabled using the start_filtering() function at + * the &dmx_demux. + * Any TS packets that match the filter settings are copied to a circular + * buffer. The filtered TS packets are delivered to the client using this + * callback function. The size of the circular buffer is controlled by the + * circular_buffer_size parameter of the &dmx_ts_feed.@set function. + * It is expected that the @buffer1 and @buffer2 callback parameters point to + * addresses within the circular buffer, but other implementations are also + * possible. Note that the called party should not try to free the memory + * the @buffer1 and @buffer2 parameters point to. + * + * When this function is called, the @buffer1 parameter typically points to + * the start of the first undelivered TS packet within a circular buffer. + * The @buffer2 buffer parameter is normally NULL, except when the received + * TS packets have crossed the last address of the circular buffer and + * ”wrapped” to the beginning of the buffer. In the latter case the @buffer1 + * parameter would contain an address within the circular buffer, while the + * @buffer2 parameter would contain the first address of the circular buffer. + * The number of bytes delivered with this function (i.e. @buffer1_length + + * @buffer2_length) is usually equal to the value of callback_length parameter + * given in the set() function, with one exception: if a timeout occurs before + * receiving callback_length bytes of TS data, any undelivered packets are + * immediately delivered to the client by calling this function. The timeout + * duration is controlled by the set() function in the TS Feed API. + * + * If a TS packet is received with errors that could not be fixed by the + * TS-level forward error correction (FEC), the Transport_error_indicator + * flag of the TS packet header should be set. The TS packet should not be + * discarded, as the error can possibly be corrected by a higher layer + * protocol. If the called party is slow in processing the callback, it + * is possible that the circular buffer eventually fills up. If this happens, + * the demux driver should discard any TS packets received while the buffer + * is full and return -EOVERFLOW. + * + * The type of data returned to the callback can be selected by the + * &dmx_ts_feed.@set function. The type parameter decides if the raw + * TS packet (TS_PACKET) or just the payload (TS_PACKET|TS_PAYLOAD_ONLY) + * should be returned. If additionally the TS_DECODER bit is set the stream + * will also be sent to the hardware MPEG decoder. + * + * Return: + * 0, on success; + * -EOVERFLOW, on buffer overflow. + */ +typedef int (*dmx_ts_cb)(const u8 *buffer1, + size_t buffer1_length, + const u8 *buffer2, + size_t buffer2_length, + struct dmx_ts_feed *source); + +/** + * typedef dmx_section_cb - DVB demux TS filter callback function prototype + * + * @buffer1: Pointer to the start of the filtered section, e.g. + * within the circular buffer of the demux driver. + * @buffer1_len: Length of the filtered section data in @buffer1, + * including headers and CRC. + * @buffer2: Pointer to the tail of the filtered section data, + * or NULL. Useful to handle the wrapping of a + * circular buffer. + * @buffer2_len: Length of the filtered section data in @buffer2, + * including headers and CRC. + * @source: Indicates which section feed is the source of the + * callback. + * + * This function callback prototype, provided by the client of the demux API, + * is called from the demux code. The function is only called when + * filtering of sections has been enabled using the function + * &dmx_ts_feed.@start_filtering. When the demux driver has received a + * complete section that matches at least one section filter, the client + * is notified via this callback function. Normally this function is called + * for each received section; however, it is also possible to deliver + * multiple sections with one callback, for example when the system load + * is high. If an error occurs while receiving a section, this + * function should be called with the corresponding error type set in the + * success field, whether or not there is data to deliver. The Section Feed + * implementation should maintain a circular buffer for received sections. + * However, this is not necessary if the Section Feed API is implemented as + * a client of the TS Feed API, because the TS Feed implementation then + * buffers the received data. The size of the circular buffer can be + * configured using the &dmx_ts_feed.@set function in the Section Feed API. + * If there is no room in the circular buffer when a new section is received, + * the section must be discarded. If this happens, the value of the success + * parameter should be DMX_OVERRUN_ERROR on the next callback. + */ +typedef int (*dmx_section_cb)(const u8 *buffer1, + size_t buffer1_len, + const u8 *buffer2, + size_t buffer2_len, + struct dmx_section_filter *source); /*--------------------------------------------------------------------------*/ /* DVB Front-End */ /*--------------------------------------------------------------------------*/ +/** + * enum dmx_frontend_source - Used to identify the type of frontend + * + * @DMX_MEMORY_FE: The source of the demux is memory. It means that + * the MPEG-TS to be filtered comes from userspace, + * via write() syscall. + * + * @DMX_FRONTEND_0: The source of the demux is a frontend connected + * to the demux. + */ enum dmx_frontend_source { DMX_MEMORY_FE, DMX_FRONTEND_0, - DMX_FRONTEND_1, - DMX_FRONTEND_2, - DMX_FRONTEND_3, - DMX_STREAM_0, /* external stream input, e.g. LVDS */ - DMX_STREAM_1, - DMX_STREAM_2, - DMX_STREAM_3 }; +/** + * struct dmx_frontend - Structure that lists the frontends associated with + * a demux + * + * @connectivity_list: List of front-ends that can be connected to a + * particular demux; + * @source: Type of the frontend. + * + * FIXME: this structure should likely be replaced soon by some + * media-controller based logic. + */ struct dmx_frontend { - struct list_head connectivity_list; /* List of front-ends that can - be connected to a particular - demux */ + struct list_head connectivity_list; enum dmx_frontend_source source; }; -/*--------------------------------------------------------------------------*/ -/* MPEG-2 TS Demux */ -/*--------------------------------------------------------------------------*/ - /* - * Flags OR'ed in the capabilities field of struct dmx_demux. + * MPEG-2 TS Demux */ -#define DMX_TS_FILTERING 1 -#define DMX_PES_FILTERING 2 -#define DMX_SECTION_FILTERING 4 -#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */ -#define DMX_CRC_CHECKING 16 -#define DMX_TS_DESCRAMBLING 32 +/** + * enum dmx_demux_caps - MPEG-2 TS Demux capabilities bitmap + * + * @DMX_TS_FILTERING: set if TS filtering is supported; + * @DMX_SECTION_FILTERING: set if section filtering is supported; + * @DMX_MEMORY_BASED_FILTERING: set if write() available. + * + * Those flags are OR'ed in the &dmx_demux.&capabilities field + */ +enum dmx_demux_caps { + DMX_TS_FILTERING = 1, + DMX_SECTION_FILTERING = 4, + DMX_MEMORY_BASED_FILTERING = 8, +}; /* * Demux resource type identifier. @@ -200,42 +358,241 @@ struct dmx_frontend { *. */ -#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list) +#define DMX_FE_ENTRY(list) \ + list_entry(list, struct dmx_frontend, connectivity_list) + +/** + * struct dmx_demux - Structure that contains the demux capabilities and + * callbacks. + * + * @capabilities: Bitfield of capability flags. + * + * @frontend: Front-end connected to the demux + * + * @priv: Pointer to private data of the API client + * + * @open: This function reserves the demux for use by the caller and, if + * necessary, initializes the demux. When the demux is no longer needed, + * the function @close should be called. It should be possible for + * multiple clients to access the demux at the same time. Thus, the + * function implementation should increment the demux usage count when + * @open is called and decrement it when @close is called. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * It returns + * 0 on success; + * -EUSERS, if maximum usage count was reached; + * -EINVAL, on bad parameter. + * + * @close: This function reserves the demux for use by the caller and, if + * necessary, initializes the demux. When the demux is no longer needed, + * the function @close should be called. It should be possible for + * multiple clients to access the demux at the same time. Thus, the + * function implementation should increment the demux usage count when + * @open is called and decrement it when @close is called. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * It returns + * 0 on success; + * -ENODEV, if demux was not in use (e. g. no users); + * -EINVAL, on bad parameter. + * + * @write: This function provides the demux driver with a memory buffer + * containing TS packets. Instead of receiving TS packets from the DVB + * front-end, the demux driver software will read packets from memory. + * Any clients of this demux with active TS, PES or Section filters will + * receive filtered data via the Demux callback API (see 0). The function + * returns when all the data in the buffer has been consumed by the demux. + * Demux hardware typically cannot read TS from memory. If this is the + * case, memory-based filtering has to be implemented entirely in software. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @buf function parameter contains a pointer to the TS data in + * kernel-space memory. + * The @count function parameter contains the length of the TS data. + * It returns + * 0 on success; + * -ERESTARTSYS, if mutex lock was interrupted; + * -EINTR, if a signal handling is pending; + * -ENODEV, if demux was removed; + * -EINVAL, on bad parameter. + * + * @allocate_ts_feed: Allocates a new TS feed, which is used to filter the TS + * packets carrying a certain PID. The TS feed normally corresponds to a + * hardware PID filter on the demux chip. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @feed function parameter contains a pointer to the TS feed API and + * instance data. + * The @callback function parameter contains a pointer to the callback + * function for passing received TS packet. + * It returns + * 0 on success; + * -ERESTARTSYS, if mutex lock was interrupted; + * -EBUSY, if no more TS feeds is available; + * -EINVAL, on bad parameter. + * + * @release_ts_feed: Releases the resources allocated with @allocate_ts_feed. + * Any filtering in progress on the TS feed should be stopped before + * calling this function. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @feed function parameter contains a pointer to the TS feed API and + * instance data. + * It returns + * 0 on success; + * -EINVAL on bad parameter. + * + * @allocate_section_feed: Allocates a new section feed, i.e. a demux resource + * for filtering and receiving sections. On platforms with hardware + * support for section filtering, a section feed is directly mapped to + * the demux HW. On other platforms, TS packets are first PID filtered in + * hardware and a hardware section filter then emulated in software. The + * caller obtains an API pointer of type dmx_section_feed_t as an out + * parameter. Using this API the caller can set filtering parameters and + * start receiving sections. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @feed function parameter contains a pointer to the TS feed API and + * instance data. + * The @callback function parameter contains a pointer to the callback + * function for passing received TS packet. + * It returns + * 0 on success; + * -EBUSY, if no more TS feeds is available; + * -EINVAL, on bad parameter. + * + * @release_section_feed: Releases the resources allocated with + * @allocate_section_feed, including allocated filters. Any filtering in + * progress on the section feed should be stopped before calling this + * function. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @feed function parameter contains a pointer to the TS feed API and + * instance data. + * It returns + * 0 on success; + * -EINVAL, on bad parameter. + * + * @add_frontend: Registers a connectivity between a demux and a front-end, + * i.e., indicates that the demux can be connected via a call to + * @connect_frontend to use the given front-end as a TS source. The + * client of this function has to allocate dynamic or static memory for + * the frontend structure and initialize its fields before calling this + * function. This function is normally called during the driver + * initialization. The caller must not free the memory of the frontend + * struct before successfully calling @remove_frontend. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @frontend function parameter contains a pointer to the front-end + * instance data. + * It returns + * 0 on success; + * -EINVAL, on bad parameter. + * + * @remove_frontend: Indicates that the given front-end, registered by a call + * to @add_frontend, can no longer be connected as a TS source by this + * demux. The function should be called when a front-end driver or a demux + * driver is removed from the system. If the front-end is in use, the + * function fails with the return value of -EBUSY. After successfully + * calling this function, the caller can free the memory of the frontend + * struct if it was dynamically allocated before the @add_frontend + * operation. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @frontend function parameter contains a pointer to the front-end + * instance data. + * It returns + * 0 on success; + * -ENODEV, if the front-end was not found, + * -EINVAL, on bad parameter. + * + * @get_frontends: Provides the APIs of the front-ends that have been + * registered for this demux. Any of the front-ends obtained with this + * call can be used as a parameter for @connect_frontend. The include + * file demux.h contains the macro DMX_FE_ENTRY() for converting an + * element of the generic type struct &list_head * to the type + * struct &dmx_frontend *. The caller must not free the memory of any of + * the elements obtained via this function call. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * It returns a struct list_head pointer to the list of front-end + * interfaces, or NULL in the case of an empty list. + * + * @connect_frontend: Connects the TS output of the front-end to the input of + * the demux. A demux can only be connected to a front-end registered to + * the demux with the function @add_frontend. It may or may not be + * possible to connect multiple demuxes to the same front-end, depending + * on the capabilities of the HW platform. When not used, the front-end + * should be released by calling @disconnect_frontend. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @frontend function parameter contains a pointer to the front-end + * instance data. + * It returns + * 0 on success; + * -EINVAL, on bad parameter. + * + * @disconnect_frontend: Disconnects the demux and a front-end previously + * connected by a @connect_frontend call. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * It returns + * 0 on success; + * -EINVAL on bad parameter. + * + * @get_pes_pids: Get the PIDs for DMX_PES_AUDIO0, DMX_PES_VIDEO0, + * DMX_PES_TELETEXT0, DMX_PES_SUBTITLE0 and DMX_PES_PCR0. + * The @demux function parameter contains a pointer to the demux API and + * instance data. + * The @pids function parameter contains an array with five u16 elements + * where the PIDs will be stored. + * It returns + * 0 on success; + * -EINVAL on bad parameter. + */ struct dmx_demux { - u32 capabilities; /* Bitfield of capability flags */ - struct dmx_frontend* frontend; /* Front-end connected to the demux */ - void* priv; /* Pointer to private data of the API client */ - int (*open) (struct dmx_demux* demux); - int (*close) (struct dmx_demux* demux); - int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count); - int (*allocate_ts_feed) (struct dmx_demux* demux, - struct dmx_ts_feed** feed, - dmx_ts_cb callback); - int (*release_ts_feed) (struct dmx_demux* demux, - struct dmx_ts_feed* feed); - int (*allocate_section_feed) (struct dmx_demux* demux, - struct dmx_section_feed** feed, - dmx_section_cb callback); - int (*release_section_feed) (struct dmx_demux* demux, - struct dmx_section_feed* feed); - int (*add_frontend) (struct dmx_demux* demux, - struct dmx_frontend* frontend); - int (*remove_frontend) (struct dmx_demux* demux, - struct dmx_frontend* frontend); - struct list_head* (*get_frontends) (struct dmx_demux* demux); - int (*connect_frontend) (struct dmx_demux* demux, - struct dmx_frontend* frontend); - int (*disconnect_frontend) (struct dmx_demux* demux); - - int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids); - - int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps); - - int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src); - - int (*get_stc) (struct dmx_demux* demux, unsigned int num, - u64 *stc, unsigned int *base); + enum dmx_demux_caps capabilities; + struct dmx_frontend *frontend; + void *priv; + int (*open)(struct dmx_demux *demux); + int (*close)(struct dmx_demux *demux); + int (*write)(struct dmx_demux *demux, const char __user *buf, + size_t count); + int (*allocate_ts_feed)(struct dmx_demux *demux, + struct dmx_ts_feed **feed, + dmx_ts_cb callback); + int (*release_ts_feed)(struct dmx_demux *demux, + struct dmx_ts_feed *feed); + int (*allocate_section_feed)(struct dmx_demux *demux, + struct dmx_section_feed **feed, + dmx_section_cb callback); + int (*release_section_feed)(struct dmx_demux *demux, + struct dmx_section_feed *feed); + int (*add_frontend)(struct dmx_demux *demux, + struct dmx_frontend *frontend); + int (*remove_frontend)(struct dmx_demux *demux, + struct dmx_frontend *frontend); + struct list_head *(*get_frontends)(struct dmx_demux *demux); + int (*connect_frontend)(struct dmx_demux *demux, + struct dmx_frontend *frontend); + int (*disconnect_frontend)(struct dmx_demux *demux); + + int (*get_pes_pids)(struct dmx_demux *demux, u16 *pids); + + /* private: Not used upstream and never documented */ +#if 0 + int (*get_caps)(struct dmx_demux *demux, struct dmx_caps *caps); + int (*set_source)(struct dmx_demux *demux, const dmx_source_t *src); +#endif + /* + * private: Only used at av7110, to read some data from firmware. + * As this was never documented, we have no clue about what's + * there, and its usage on other drivers aren't encouraged. + */ + int (*get_stc)(struct dmx_demux *demux, unsigned int num, + u64 *stc, unsigned int *base); }; #endif /* #ifndef __DEMUX_H */ diff --git a/kernel/drivers/media/dvb-core/dmxdev.c b/kernel/drivers/media/dvb-core/dmxdev.c index d0e3f9d85..ea9abde90 100644 --- a/kernel/drivers/media/dvb-core/dmxdev.c +++ b/kernel/drivers/media/dvb-core/dmxdev.c @@ -352,8 +352,7 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter) static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_section_filter *filter, - enum dmx_success success) + struct dmx_section_filter *filter) { struct dmxdev_filter *dmxdevfilter = filter->priv; int ret; @@ -386,8 +385,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_ts_feed *feed, - enum dmx_success success) + struct dmx_ts_feed *feed) { struct dmxdev_filter *dmxdevfilter = feed->priv; struct dvb_ringbuffer *buffer; @@ -1023,6 +1021,9 @@ static int dvb_demux_do_ioctl(struct file *file, dmxdev->demux->get_pes_pids(dmxdev->demux, parg); break; +#if 0 + /* Not used upstream and never documented */ + case DMX_GET_CAPS: if (!dmxdev->demux->get_caps) { ret = -EINVAL; @@ -1038,6 +1039,7 @@ static int dvb_demux_do_ioctl(struct file *file, } ret = dmxdev->demux->set_source(dmxdev->demux, parg); break; +#endif case DMX_GET_STC: if (!dmxdev->demux->get_stc) { diff --git a/kernel/drivers/media/dvb-core/dvb-usb-ids.h b/kernel/drivers/media/dvb-core/dvb-usb-ids.h index c117fb3b4..0a46580b5 100644 --- a/kernel/drivers/media/dvb-core/dvb-usb-ids.h +++ b/kernel/drivers/media/dvb-core/dvb-usb-ids.h @@ -257,6 +257,7 @@ #define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab #define USB_PID_TERRATEC_H7 0x10b4 #define USB_PID_TERRATEC_H7_2 0x10a3 +#define USB_PID_TERRATEC_H7_3 0x10a5 #define USB_PID_TERRATEC_T3 0x10a0 #define USB_PID_TERRATEC_T5 0x10a1 #define USB_PID_NOXON_DAB_STICK 0x00b3 diff --git a/kernel/drivers/media/dvb-core/dvb_ca_en50221.c b/kernel/drivers/media/dvb-core/dvb_ca_en50221.c index 72937756f..fb66184dc 100644 --- a/kernel/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/kernel/drivers/media/dvb-core/dvb_ca_en50221.c @@ -169,10 +169,10 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * e /** * Safely find needle in haystack. * - * @param haystack Buffer to look in. - * @param hlen Number of bytes in haystack. - * @param needle Buffer to find. - * @param nlen Number of bytes in needle. + * @haystack: Buffer to look in. + * @hlen: Number of bytes in haystack. + * @needle: Buffer to find. + * @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) @@ -197,7 +197,7 @@ static char *findstr(char * haystack, int hlen, char * needle, int nlen) /** - * Check CAM status. + * dvb_ca_en50221_check_camstatus - Check CAM status. */ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) { @@ -240,13 +240,13 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) /** - * Wait for flags to become set on the STATUS register on a CAM interface, - * checking for errors and timeout. + * dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS + * register on a CAM interface, checking for errors and timeout. * - * @param ca CA instance. - * @param slot Slot on interface. - * @param waitfor Flags to wait for. - * @param timeout_ms Timeout in milliseconds. + * @ca: CA instance. + * @slot: Slot on interface. + * @waitfor: Flags to wait for. + * @timeout_ms: Timeout in milliseconds. * * @return 0 on success, nonzero on error. */ @@ -290,10 +290,10 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, /** - * Initialise the link layer connection to a CAM. + * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM. * - * @param ca CA instance. - * @param slot Slot id. + * @ca: CA instance. + * @slot: Slot id. * * @return 0 on success, nonzero on failure. */ @@ -346,14 +346,14 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) } /** - * Read a tuple from attribute memory. + * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory. * - * @param ca CA instance. - * @param slot Slot id. - * @param address Address to read from. Updated. - * @param tupleType Tuple id byte. Updated. - * @param tupleLength Tuple length. Updated. - * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. + * @ca: CA instance. + * @slot: Slot id. + * @address: Address to read from. Updated. + * @tupleType: Tuple id byte. Updated. + * @tupleLength: Tuple length. Updated. + * @tuple: Dest buffer for tuple (must be 256 bytes). Updated. * * @return 0 on success, nonzero on error. */ @@ -399,11 +399,11 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, /** - * Parse attribute memory of a CAM module, extracting Config register, and checking - * it is a DVB CAM module. + * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module, + * extracting Config register, and checking it is a DVB CAM module. * - * @param ca CA instance. - * @param slot Slot id. + * @ca: CA instance. + * @slot: Slot id. * * @return 0 on success, <0 on failure. */ @@ -546,10 +546,10 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot) /** - * Set CAM's configoption correctly. + * dvb_ca_en50221_set_configoption - Set CAM's configoption correctly. * - * @param ca CA instance. - * @param slot Slot containing the CAM. + * @ca: CA instance. + * @slot: Slot containing the CAM. */ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) { @@ -574,15 +574,16 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) /** - * This function talks to an EN50221 CAM control interface. It reads a buffer of - * data from the CAM. The data can either be stored in a supplied buffer, or - * automatically be added to the slot's rx_buffer. + * dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control + * interface. It reads a buffer of data from the CAM. The data can either + * be stored in a supplied buffer, or automatically be added to the slot's + * rx_buffer. * - * @param ca CA instance. - * @param slot Slot to read from. - * @param ebuf If non-NULL, the data will be written to this buffer. If NULL, + * @ca: CA instance. + * @slot: Slot to read from. + * @ebuf: If non-NULL, the data will be written to this buffer. If NULL, * the data will be added into the buffering system as a normal fragment. - * @param ecount Size of ebuf. Ignored if ebuf is NULL. + * @ecount: Size of ebuf. Ignored if ebuf is NULL. * * @return Number of bytes read, or < 0 on error */ @@ -698,14 +699,14 @@ exit: /** - * This function talks to an EN50221 CAM control interface. It writes a buffer of data - * to a CAM. + * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control + * interface. It writes a buffer of data to a CAM. * - * @param ca CA instance. - * @param slot Slot to write to. - * @param ebuf The data in this buffer is treated as a complete link-level packet to + * @ca: CA instance. + * @slot: Slot to write to. + * @ebuf: The data in this buffer is treated as a complete link-level packet to * be written. - * @param count Size of ebuf. + * @count: Size of ebuf. * * @return Number of bytes written, or < 0 on error. */ @@ -790,10 +791,10 @@ EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); /** - * A CAM has been removed => shut it down. + * dvb_ca_en50221_camready_irq - A CAM has been removed => shut it down. * - * @param ca CA instance. - * @param slot Slot to shut down. + * @ca: CA instance. + * @slot: Slot to shut down. */ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot) { @@ -815,11 +816,11 @@ EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); /** - * A CAMCHANGE IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMCHANGE IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. - * @param change_type One of the DVB_CA_CAMCHANGE_* values. + * @ca: CA instance. + * @slot: Slot concerned. + * @change_type: One of the DVB_CA_CAMCHANGE_* values. */ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type) { @@ -844,10 +845,10 @@ EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); /** - * A CAMREADY IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) { @@ -865,8 +866,8 @@ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) /** * An FR or DA IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) { @@ -899,7 +900,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) /** * Wake up the DVB CA thread * - * @param ca CA instance. + * @ca: CA instance. */ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) { @@ -914,7 +915,7 @@ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) /** * Update the delay used by the thread. * - * @param ca CA instance. + * @ca: CA instance. */ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) { @@ -1177,10 +1178,10 @@ static int dvb_ca_en50221_thread(void *data) * Real ioctl implementation. * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them. * - * @param inode Inode concerned. - * @param file File concerned. - * @param cmd IOCTL command. - * @param arg Associated argument. + * @inode: Inode concerned. + * @file: File concerned. + * @cmd: IOCTL command. + * @arg: Associated argument. * * @return 0 on success, <0 on error. */ @@ -1258,10 +1259,10 @@ out_unlock: /** * Wrapper for ioctl implementation. * - * @param inode Inode concerned. - * @param file File concerned. - * @param cmd IOCTL command. - * @param arg Associated argument. + * @inode: Inode concerned. + * @file: File concerned. + * @cmd: IOCTL command. + * @arg: Associated argument. * * @return 0 on success, <0 on error. */ @@ -1275,10 +1276,10 @@ static long dvb_ca_en50221_io_ioctl(struct file *file, /** * Implementation of write() syscall. * - * @param file File structure. - * @param buf Source buffer. - * @param count Size of source buffer. - * @param ppos Position in file (ignored). + * @file: File structure. + * @buf: Source buffer. + * @count: Size of source buffer. + * @ppos: Position in file (ignored). * * @return Number of bytes read, or <0 on error. */ @@ -1416,10 +1417,10 @@ nextslot: /** * Implementation of read() syscall. * - * @param file File structure. - * @param buf Destination buffer. - * @param count Size of destination buffer. - * @param ppos Position in file (ignored). + * @file: File structure. + * @buf: Destination buffer. + * @count: Size of destination buffer. + * @ppos: Position in file (ignored). * * @return Number of bytes read, or <0 on error. */ @@ -1519,8 +1520,8 @@ exit: /** * Implementation of file open syscall. * - * @param inode Inode concerned. - * @param file File concerned. + * @inode: Inode concerned. + * @file: File concerned. * * @return 0 on success, <0 on failure. */ @@ -1564,8 +1565,8 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) /** * Implementation of file close syscall. * - * @param inode Inode concerned. - * @param file File concerned. + * @inode: Inode concerned. + * @file: File concerned. * * @return 0 on success, <0 on failure. */ @@ -1592,8 +1593,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) /** * Implementation of poll() syscall. * - * @param file File concerned. - * @param wait poll wait table. + * @file: File concerned. + * @wait: poll wait table. * * @return Standard poll mask. */ @@ -1656,10 +1657,10 @@ static const struct dvb_device dvbdev_ca = { /** * Initialise a new DVB CA EN50221 interface device. * - * @param dvb_adapter DVB adapter to attach the new CA device to. - * @param ca The dvb_ca instance. - * @param flags Flags describing the CA device (DVB_CA_FLAG_*). - * @param slot_count Number of slots supported. + * @dvb_adapter: DVB adapter to attach the new CA device to. + * @ca: The dvb_ca instance. + * @flags: Flags describing the CA device (DVB_CA_FLAG_*). + * @slot_count: Number of slots supported. * * @return 0 on success, nonzero on failure */ @@ -1743,8 +1744,8 @@ EXPORT_SYMBOL(dvb_ca_en50221_release); /** * Release a DVB CA EN50221 interface device. * - * @param ca_dev The dvb_device_t instance for the CA device. - * @param ca The associated dvb_ca instance. + * @ca_dev: The dvb_device_t instance for the CA device. + * @ca: The associated dvb_ca instance. */ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) { diff --git a/kernel/drivers/media/dvb-core/dvb_ca_en50221.h b/kernel/drivers/media/dvb-core/dvb_ca_en50221.h index 7df2e1411..1e4bbbd34 100644 --- a/kernel/drivers/media/dvb-core/dvb_ca_en50221.h +++ b/kernel/drivers/media/dvb-core/dvb_ca_en50221.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _DVB_CA_EN50221_H_ @@ -37,100 +33,103 @@ #define DVB_CA_EN50221_CAMCHANGE_REMOVED 0 #define DVB_CA_EN50221_CAMCHANGE_INSERTED 1 - - -/* Structure describing a CA interface */ +/** + * struct dvb_ca_en50221- Structure describing a CA interface + * + * @owner: the module owning this structure + * @read_attribute_mem: function for reading attribute memory on the CAM + * @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 + * @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 + * @poll_slot_status: function to poll slot status. Only necessary if + * DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set. + * @data: private data, used by caller. + * @private: Opaque data used by the dvb_ca core. Do not modify! + * + * NOTE: the read_*, write_* and poll_slot_status functions will be + * called for different slots concurrently and need to use locks where + * and if appropriate. There will be no concurrent access to one slot. + */ struct dvb_ca_en50221 { + struct module *owner; - /* the module owning this structure */ - struct module* owner; - - /* NOTE: the read_*, write_* and poll_slot_status functions will be - * called for different slots concurrently and need to use locks where - * and if appropriate. There will be no concurrent access to one slot. - */ + int (*read_attribute_mem)(struct dvb_ca_en50221 *ca, + int slot, int address); + int (*write_attribute_mem)(struct dvb_ca_en50221 *ca, + int slot, int address, u8 value); - /* functions for accessing attribute memory on the CAM */ - int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address); - int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value); + int (*read_cam_control)(struct dvb_ca_en50221 *ca, + int slot, u8 address); + int (*write_cam_control)(struct dvb_ca_en50221 *ca, + int slot, u8 address, u8 value); - /* functions for accessing the control interface on the CAM */ - int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address); - int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value); + 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); - /* Functions for controlling slots */ - 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); + int (*poll_slot_status)(struct dvb_ca_en50221 *ca, int slot, int open); - /* - * Poll slot status. - * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set - */ - int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open); + void *data; - /* private data, used by caller */ - void* data; - - /* Opaque data used by the dvb_ca core. Do not modify! */ - void* private; + void *private; }; - - - -/* ******************************************************************************** */ -/* Functions for reporting IRQ events */ +/* + * Functions for reporting IRQ events + */ /** - * A CAMCHANGE IRQ has occurred. + * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. - * @param change_type One of the DVB_CA_CAMCHANGE_* values + * @pubca: CA instance. + * @slot: Slot concerned. + * @change_type: One of the DVB_CA_CAMCHANGE_* values */ -void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type); +void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, + int change_type); /** - * A CAMREADY IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @pubca: CA instance. + * @slot: Slot concerned. */ -void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot); +void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot); /** - * An FR or a DA IRQ has occurred. + * dvb_ca_en50221_frda_irq - An FR or a DA IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ -void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot); - +void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *ca, int slot); - -/* ******************************************************************************** */ -/* Initialisation/shutdown functions */ +/* + * Initialisation/shutdown functions + */ /** - * Initialise a new DVB CA device. + * dvb_ca_en50221_init - Initialise a new DVB CA device. * - * @param dvb_adapter DVB adapter to attach the new CA device to. - * @param ca The dvb_ca instance. - * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*). - * @param slot_count Number of slots supported. + * @dvb_adapter: DVB adapter to attach the new CA device to. + * @ca: The dvb_ca instance. + * @flags: Flags describing the CA device (DVB_CA_EN50221_FLAG_*). + * @slot_count: Number of slots supported. * * @return 0 on success, nonzero on failure */ -extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count); +extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, + struct dvb_ca_en50221 *ca, int flags, + int slot_count); /** - * Release a DVB CA device. + * dvb_ca_en50221_release - Release a DVB CA device. * - * @param ca The associated dvb_ca instance. + * @ca: The associated dvb_ca instance. */ -extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca); - - +extern void dvb_ca_en50221_release(struct dvb_ca_en50221 *ca); #endif diff --git a/kernel/drivers/media/dvb-core/dvb_demux.c b/kernel/drivers/media/dvb-core/dvb_demux.c index 6c7ff0cdc..0cc5e9351 100644 --- a/kernel/drivers/media/dvb-core/dvb_demux.c +++ b/kernel/drivers/media/dvb-core/dvb_demux.c @@ -130,7 +130,7 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, feed->peslen += count; - return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK); + return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts); } static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, @@ -152,7 +152,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, return 0; return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, - NULL, 0, &f->filter, DMX_OK); + NULL, 0, &f->filter); } static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) @@ -367,8 +367,7 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, if (feed->ts_type & TS_PAYLOAD_ONLY) dvb_dmx_swfilter_payload(feed, buf); else - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, - DMX_OK); + feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts); } if (feed->ts_type & TS_DECODER) if (feed->demux->write_to_decoder) @@ -469,7 +468,7 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) if (feed->pid == pid) dvb_dmx_swfilter_packet_type(feed, buf); else if (feed->pid == 0x2000) - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); + feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts); } } @@ -588,7 +587,7 @@ void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) spin_lock_irqsave(&demux->lock, flags); - demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK); + demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts); spin_unlock_irqrestore(&demux->lock, flags); } diff --git a/kernel/drivers/media/dvb-core/dvb_frontend.c b/kernel/drivers/media/dvb-core/dvb_frontend.c index 882ca417f..e2a383317 100644 --- a/kernel/drivers/media/dvb-core/dvb_frontend.c +++ b/kernel/drivers/media/dvb-core/dvb_frontend.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include "dvb_frontend.h" @@ -80,7 +81,6 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open( #define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW) #define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW) -#define FE_ALGO_HW 1 /* * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling. * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune. @@ -110,7 +110,7 @@ struct dvb_frontend_private { struct task_struct *thread; unsigned long release_jiffies; unsigned int wakeup; - fe_status_t status; + enum fe_status status; unsigned long tune_mode_flags; unsigned int delay; unsigned int reinitialise; @@ -198,7 +198,8 @@ static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system) } } -static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) +static void dvb_frontend_add_event(struct dvb_frontend *fe, + enum fe_status status) { struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dvb_fe_events *events = &fepriv->events; @@ -429,7 +430,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra static void dvb_frontend_swzigzag(struct dvb_frontend *fe) { - fe_status_t s = 0; + enum fe_status s = 0; int retval = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp; @@ -690,7 +691,7 @@ static int dvb_frontend_thread(void *data) { struct dvb_frontend *fe = data; struct dvb_frontend_private *fepriv = fe->frontend_priv; - fe_status_t s; + enum fe_status s; enum dvbfe_algo algo; #ifdef CONFIG_MEDIA_CONTROLLER_DVB int ret; @@ -889,42 +890,21 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) fepriv->thread); } -s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime) -{ - return ((curtime.tv_usec < lasttime.tv_usec) ? - 1000000 - lasttime.tv_usec + curtime.tv_usec : - curtime.tv_usec - lasttime.tv_usec); -} -EXPORT_SYMBOL(timeval_usec_diff); - -static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec) -{ - curtime->tv_usec += add_usec; - if (curtime->tv_usec >= 1000000) { - curtime->tv_usec -= 1000000; - curtime->tv_sec++; - } -} - /* * Sleep until gettimeofday() > waketime + add_usec * This needs to be as precise as possible, but as the delay is * usually between 2ms and 32ms, it is done using a scheduled msleep * followed by usleep (normally a busy-wait loop) for the remainder */ -void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec) +void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec) { - struct timeval lasttime; s32 delta, newdelta; - timeval_usec_add(waketime, add_usec); - - do_gettimeofday(&lasttime); - delta = timeval_usec_diff(lasttime, *waketime); + ktime_add_us(*waketime, add_usec); + delta = ktime_us_delta(ktime_get_real(), *waketime); if (delta > 2500) { msleep((delta - 1500) / 1000); - do_gettimeofday(&lasttime); - newdelta = timeval_usec_diff(lasttime, *waketime); + newdelta = ktime_us_delta(ktime_get_real(), *waketime); delta = (newdelta > delta) ? 0 : newdelta; } if (delta > 0) @@ -2216,7 +2196,7 @@ static int dtv_set_frontend(struct dvb_frontend *fe) break; } if (rolloff) - c->bandwidth_hz = (c->symbol_rate * rolloff) / 100; + c->bandwidth_hz = mult_frac(c->symbol_rate, rolloff, 100); /* force auto frequency inversion if requested */ if (dvb_force_auto_inversion) @@ -2333,15 +2313,15 @@ static int dvb_frontend_ioctl_legacy(struct file *file, dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n", __func__, c->delivery_system, fe->ops.info.type); - /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't - * do it, it is done for it. */ - info->caps |= FE_CAN_INVERSION_AUTO; + /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */ + if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) + info->caps |= FE_CAN_INVERSION_AUTO; err = 0; break; } case FE_READ_STATUS: { - fe_status_t* status = parg; + enum fe_status *status = parg; /* if retune was requested but hasn't occurred yet, prevent * that user get signal state from previous tuning */ @@ -2403,7 +2383,13 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_DISEQC_SEND_MASTER_CMD: if (fe->ops.diseqc_send_master_cmd) { - err = fe->ops.diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg); + struct dvb_diseqc_master_cmd *cmd = parg; + + if (cmd->msg_len > sizeof(cmd->msg)) { + err = -EINVAL; + break; + } + err = fe->ops.diseqc_send_master_cmd(fe, cmd); fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -2411,7 +2397,8 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_DISEQC_SEND_BURST: if (fe->ops.diseqc_send_burst) { - err = fe->ops.diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg); + err = fe->ops.diseqc_send_burst(fe, + (enum fe_sec_mini_cmd)parg); fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -2419,8 +2406,9 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_SET_TONE: if (fe->ops.set_tone) { - err = fe->ops.set_tone(fe, (fe_sec_tone_mode_t) parg); - fepriv->tone = (fe_sec_tone_mode_t) parg; + err = fe->ops.set_tone(fe, + (enum fe_sec_tone_mode)parg); + fepriv->tone = (enum fe_sec_tone_mode)parg; fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -2428,8 +2416,9 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_SET_VOLTAGE: if (fe->ops.set_voltage) { - err = fe->ops.set_voltage(fe, (fe_sec_voltage_t) parg); - fepriv->voltage = (fe_sec_voltage_t) parg; + err = fe->ops.set_voltage(fe, + (enum fe_sec_voltage)parg); + fepriv->voltage = (enum fe_sec_voltage)parg; fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -2437,7 +2426,8 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_DISHNETWORK_SEND_LEGACY_CMD: if (fe->ops.dishnetwork_send_legacy_command) { - err = fe->ops.dishnetwork_send_legacy_command(fe, (unsigned long) parg); + err = fe->ops.dishnetwork_send_legacy_command(fe, + (unsigned long)parg); fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } else if (fe->ops.set_voltage) { @@ -2458,13 +2448,13 @@ static int dvb_frontend_ioctl_legacy(struct file *file, * include the initialization or start bit */ unsigned long swcmd = ((unsigned long) parg) << 1; - struct timeval nexttime; - struct timeval tv[10]; + ktime_t nexttime; + ktime_t tv[10]; int i; u8 last = 1; if (dvb_frontend_debug) printk("%s switch command: 0x%04lx\n", __func__, swcmd); - do_gettimeofday(&nexttime); + nexttime = ktime_get_real(); if (dvb_frontend_debug) tv[0] = nexttime; /* before sending a command, initialize by sending @@ -2475,7 +2465,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, for (i = 0; i < 9; i++) { if (dvb_frontend_debug) - do_gettimeofday(&tv[i + 1]); + tv[i+1] = ktime_get_real(); if ((swcmd & 0x01) != last) { /* set voltage to (last ? 13V : 18V) */ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); @@ -2489,7 +2479,8 @@ static int dvb_frontend_ioctl_legacy(struct file *file, printk("%s(%d): switch delay (should be 32k followed by all 8k\n", __func__, fe->dvb->num); for (i = 1; i < 10; i++) - printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); + printk("%d: %d\n", i, + (int) ktime_us_delta(tv[i], tv[i-1])); } err = 0; fepriv->state = FESTATE_DISEQC; diff --git a/kernel/drivers/media/dvb-core/dvb_frontend.h b/kernel/drivers/media/dvb-core/dvb_frontend.h index 816269e5f..97661b2f2 100644 --- a/kernel/drivers/media/dvb-core/dvb_frontend.h +++ b/kernel/drivers/media/dvb-core/dvb_frontend.h @@ -48,6 +48,15 @@ */ #define MAX_DELSYS 8 +/** + * struct dvb_frontend_tune_settings - parameters to adjust frontend tuning + * + * @min_delay_ms: minimum delay for tuning, in ms + * @step_size: step size between two consecutive frequencies + * @max_drift: maximum drift + * + * NOTE: step_size is in Hz, for terrestrial/cable or kHz for satellite + */ struct dvb_frontend_tune_settings { int min_delay_ms; int step_size; @@ -56,6 +65,20 @@ struct dvb_frontend_tune_settings { struct dvb_frontend; +/** + * struct dvb_tuner_info - Frontend name and min/max ranges/bandwidths + * + * @name: name of the Frontend + * @frequency_min: minimal frequency supported + * @frequency_max: maximum frequency supported + * @frequency_step: frequency step + * @bandwidth_min: minimal frontend bandwidth supported + * @bandwidth_max: maximum frontend bandwidth supported + * @bandwidth_step: frontend bandwidth step + * + * NOTE: frequency parameters are in Hz, for terrestrial/cable or kHz for + * satellite. + */ struct dvb_tuner_info { char name[128]; @@ -68,6 +91,20 @@ struct dvb_tuner_info { u32 bandwidth_step; }; +/** + * struct analog_parameters - Parameters to tune into an analog/radio channel + * + * @frequency: Frequency used by analog TV tuner (either in 62.5 kHz step, + * for TV, or 62.5 Hz for radio) + * @mode: Tuner mode, as defined on enum v4l2_tuner_type + * @audmode: Audio mode as defined for the rxsubchans field at videodev2.h, + * e. g. V4L2_TUNER_MODE_* + * @std: TV standard bitmap as defined at videodev2.h, e. g. V4L2_STD_* + * + * Hybrid tuners should be supported by both V4L2 and DVB APIs. This + * struct contains the data that are used by the V4L2 side. To avoid + * dependencies from V4L2 headers, all enums here are declared as integers. + */ struct analog_parameters { unsigned int frequency; unsigned int mode; @@ -75,42 +112,6 @@ struct analog_parameters { u64 std; }; -enum dvbfe_modcod { - DVBFE_MODCOD_DUMMY_PLFRAME = 0, - DVBFE_MODCOD_QPSK_1_4, - DVBFE_MODCOD_QPSK_1_3, - DVBFE_MODCOD_QPSK_2_5, - DVBFE_MODCOD_QPSK_1_2, - DVBFE_MODCOD_QPSK_3_5, - DVBFE_MODCOD_QPSK_2_3, - DVBFE_MODCOD_QPSK_3_4, - DVBFE_MODCOD_QPSK_4_5, - DVBFE_MODCOD_QPSK_5_6, - DVBFE_MODCOD_QPSK_8_9, - DVBFE_MODCOD_QPSK_9_10, - DVBFE_MODCOD_8PSK_3_5, - DVBFE_MODCOD_8PSK_2_3, - DVBFE_MODCOD_8PSK_3_4, - DVBFE_MODCOD_8PSK_5_6, - DVBFE_MODCOD_8PSK_8_9, - DVBFE_MODCOD_8PSK_9_10, - DVBFE_MODCOD_16APSK_2_3, - DVBFE_MODCOD_16APSK_3_4, - DVBFE_MODCOD_16APSK_4_5, - DVBFE_MODCOD_16APSK_5_6, - DVBFE_MODCOD_16APSK_8_9, - DVBFE_MODCOD_16APSK_9_10, - DVBFE_MODCOD_32APSK_3_4, - DVBFE_MODCOD_32APSK_4_5, - DVBFE_MODCOD_32APSK_5_6, - DVBFE_MODCOD_32APSK_8_9, - DVBFE_MODCOD_32APSK_9_10, - DVBFE_MODCOD_RESERVED_1, - DVBFE_MODCOD_BPSK_1_3, - DVBFE_MODCOD_BPSK_1_4, - DVBFE_MODCOD_RESERVED_2 -}; - enum tuner_param { DVBFE_TUNER_FREQUENCY = (1 << 0), DVBFE_TUNER_TUNERSTEP = (1 << 1), @@ -121,30 +122,28 @@ enum tuner_param { DVBFE_TUNER_DUMMY = (1 << 31) }; -/* - * ALGO_HW: (Hardware Algorithm) - * ---------------------------------------------------------------- - * Devices that support this algorithm do everything in hardware - * and no software support is needed to handle them. - * Requesting these devices to LOCK is the only thing required, - * device is supposed to do everything in the hardware. - * - * ALGO_SW: (Software Algorithm) - * ---------------------------------------------------------------- +/** + * enum dvbfe_algo - defines the algorithm used to tune into a channel + * + * @DVBFE_ALGO_HW: Hardware Algorithm - + * Devices that support this algorithm do everything in hardware + * and no software support is needed to handle them. + * Requesting these devices to LOCK is the only thing required, + * device is supposed to do everything in the hardware. + * + * @DVBFE_ALGO_SW: Software Algorithm - * These are dumb devices, that require software to do everything * - * ALGO_CUSTOM: (Customizable Agorithm) - * ---------------------------------------------------------------- - * Devices having this algorithm can be customized to have specific - * algorithms in the frontend driver, rather than simply doing a - * software zig-zag. In this case the zigzag maybe hardware assisted - * or it maybe completely done in hardware. In all cases, usage of - * this algorithm, in conjunction with the search and track - * callbacks, utilizes the driver specific algorithm. - * - * ALGO_RECOVERY: (Recovery Algorithm) - * ---------------------------------------------------------------- - * These devices have AUTO recovery capabilities from LOCK failure + * @DVBFE_ALGO_CUSTOM: Customizable Agorithm - + * Devices having this algorithm can be customized to have specific + * algorithms in the frontend driver, rather than simply doing a + * software zig-zag. In this case the zigzag maybe hardware assisted + * or it maybe completely done in hardware. In all cases, usage of + * this algorithm, in conjunction with the search and track + * callbacks, utilizes the driver specific algorithm. + * + * @DVBFE_ALGO_RECOVERY: Recovery Algorithm - + * These devices have AUTO recovery capabilities from LOCK failure */ enum dvbfe_algo { DVBFE_ALGO_HW = (1 << 0), @@ -162,27 +161,27 @@ struct tuner_state { u32 refclock; }; -/* - * search callback possible return status +/** + * enum dvbfe_search - search callback possible return status * - * DVBFE_ALGO_SEARCH_SUCCESS - * The frontend search algorithm completed and returned successfully + * @DVBFE_ALGO_SEARCH_SUCCESS: + * The frontend search algorithm completed and returned successfully * - * DVBFE_ALGO_SEARCH_ASLEEP - * The frontend search algorithm is sleeping + * @DVBFE_ALGO_SEARCH_ASLEEP: + * The frontend search algorithm is sleeping * - * DVBFE_ALGO_SEARCH_FAILED - * The frontend search for a signal failed + * @DVBFE_ALGO_SEARCH_FAILED: + * The frontend search for a signal failed * - * DVBFE_ALGO_SEARCH_INVALID - * The frontend search algorith was probably supplied with invalid - * parameters and the search is an invalid one + * @DVBFE_ALGO_SEARCH_INVALID: + * The frontend search algorith was probably supplied with invalid + * parameters and the search is an invalid one * - * DVBFE_ALGO_SEARCH_ERROR - * The frontend search algorithm failed due to some error + * @DVBFE_ALGO_SEARCH_ERROR: + * The frontend search algorithm failed due to some error * - * DVBFE_ALGO_SEARCH_AGAIN - * The frontend search algorithm was requested to search again + * @DVBFE_ALGO_SEARCH_AGAIN: + * The frontend search algorithm was requested to search again */ enum dvbfe_search { DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0), @@ -193,7 +192,56 @@ enum dvbfe_search { DVBFE_ALGO_SEARCH_ERROR = (1 << 31), }; - +/** + * struct dvb_tuner_ops - Tuner information and callbacks + * + * @info: embedded struct dvb_tuner_info with tuner properties + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @init: callback function used to initialize the tuner device. + * @sleep: callback function used to put the tuner to sleep. + * @suspend: callback function used to inform that the Kernel will + * suspend. + * @resume: callback function used to inform that the Kernel is + * resuming from suspend. + * @set_params: callback function used to inform the tuner to tune + * into a digital TV channel. The properties to be used + * are stored at @dvb_frontend.dtv_property_cache;. The + * tuner demod can change the parameters to reflect the + * changes needed for the channel to be tuned, and + * update statistics. + * @set_analog_params: callback function used to tune into an analog TV + * channel on hybrid tuners. It passes @analog_parameters; + * to the driver. + * @calc_regs: callback function used to pass register data settings + * for simple tuners. + * @set_config: callback function used to send some tuner-specific + * parameters. + * @get_frequency: get the actual tuned frequency + * @get_bandwidth: get the bandwitdh used by the low pass filters + * @get_if_frequency: get the Intermediate Frequency, in Hz. For baseband, + * should return 0. + * @get_status: returns the frontend lock status + * @get_rf_strength: returns the RF signal strengh. Used mostly to support + * analog TV and radio. Digital TV should report, instead, + * via DVBv5 API (@dvb_frontend.dtv_property_cache;). + * @get_afc: Used only by analog TV core. Reports the frequency + * drift due to AFC. + * @set_frequency: Set a new frequency. Please notice that using + * set_params is preferred. + * @set_bandwidth: Set a new frequency. Please notice that using + * set_params is preferred. + * @set_state: callback function used on some legacy drivers that + * don't implement set_params in order to set properties. + * Shouldn't be used on new drivers. + * @get_state: callback function used to get properties by some + * legacy drivers that don't implement set_params. + * Shouldn't be used on new drivers. + * + * NOTE: frequencies used on get_frequency and set_frequency are in Hz for + * terrestrial/cable or kHz for satellite. + * + */ struct dvb_tuner_ops { struct dvb_tuner_info info; @@ -237,10 +285,37 @@ struct dvb_tuner_ops { int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); }; +/** + * struct analog_demod_info - Information struct for analog TV part of the demod + * + * @name: Name of the analog TV demodulator + */ struct analog_demod_info { char *name; }; +/** + * struct analog_demod_ops - Demodulation information and callbacks for + * analog TV and radio + * + * @info: pointer to struct analog_demod_info + * @set_params: callback function used to inform the demod to set the + * demodulator parameters needed to decode an analog or + * radio channel. The properties are passed via + * struct @analog_params;. + * @has_signal: returns 0xffff if has signal, or 0 if it doesn't. + * @get_afc: Used only by analog TV core. Reports the frequency + * drift due to AFC. + * @tuner_status: callback function that returns tuner status bits, e. g. + * TUNER_STATUS_LOCKED and TUNER_STATUS_STEREO. + * @standby: set the tuner to standby mode. + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C + * mux support instead. + * @set_config: callback function used to send some tuner-specific + * parameters. + */ struct analog_demod_ops { struct analog_demod_info info; @@ -260,6 +335,87 @@ struct analog_demod_ops { struct dtv_frontend_properties; + +/** + * struct dvb_frontend_ops - Demodulation information and callbacks for + * ditialt TV + * + * @info: embedded struct dvb_tuner_info with tuner properties + * @delsys: Delivery systems supported by the frontend + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @release_sec: callback function requesting that the Satelite Equipment + * Control (SEC) driver to release and free any memory + * allocated by the driver. + * @init: callback function used to initialize the tuner device. + * @sleep: callback function used to put the tuner to sleep. + * @write: callback function used by some demod legacy drivers to + * allow other drivers to write data into their registers. + * Should not be used on new drivers. + * @tune: callback function used by demod drivers that use + * @DVBFE_ALGO_HW; to tune into a frequency. + * @get_frontend_algo: returns the desired hardware algorithm. + * @set_frontend: callback function used to inform the demod to set the + * parameters for demodulating a digital TV channel. + * The properties to be used are stored at + * @dvb_frontend.dtv_property_cache;. The demod can change + * the parameters to reflect the changes needed for the + * channel to be decoded, and update statistics. + * @get_tune_settings: callback function + * @get_frontend: callback function used to inform the parameters + * actuall in use. The properties to be used are stored at + * @dvb_frontend.dtv_property_cache; and update + * statistics. Please notice that it should not return + * an error code if the statistics are not available + * because the demog is not locked. + * @read_status: returns the locking status of the frontend. + * @read_ber: legacy callback function to return the bit error rate. + * Newer drivers should provide such info via DVBv5 API, + * e. g. @set_frontend;/@get_frontend;, implementing this + * callback only if DVBv3 API compatibility is wanted. + * @read_signal_strength: legacy callback function to return the signal + * strength. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @read_snr: legacy callback function to return the Signal/Noise + * rate. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @read_ucblocks: legacy callback function to return the Uncorrected Error + * Blocks. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @diseqc_reset_overload: callback function to implement the + * FE_DISEQC_RESET_OVERLOAD ioctl (only Satellite) + * @diseqc_send_master_cmd: callback function to implement the + * FE_DISEQC_SEND_MASTER_CMD ioctl (only Satellite). + * @diseqc_recv_slave_reply: callback function to implement the + * FE_DISEQC_RECV_SLAVE_REPLY ioctl (only Satellite) + * @diseqc_send_burst: callback function to implement the + * FE_DISEQC_SEND_BURST ioctl (only Satellite). + * @set_tone: callback function to implement the + * FE_SET_TONE ioctl (only Satellite). + * @set_voltage: callback function to implement the + * FE_SET_VOLTAGE ioctl (only Satellite). + * @enable_high_lnb_voltage: callback function to implement the + * FE_ENABLE_HIGH_LNB_VOLTAGE ioctl (only Satellite). + * @dishnetwork_send_legacy_command: callback function to implement the + * FE_DISHNETWORK_SEND_LEGACY_CMD ioctl (only Satellite). + * @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C + * mux support instead. + * @ts_bus_ctrl: callback function used to take control of the TS bus. + * @set_lna: callback function to power on/off/auto the LNA. + * @search: callback function used on some custom algo search algos. + * @tuner_ops: pointer to struct dvb_tuner_ops + * @analog_ops: pointer to struct analog_demod_ops + * @set_property: callback function to allow the frontend to validade + * incoming properties. Should not be used on new drivers. + * @get_property: callback function to allow the frontend to override + * outcoming properties. Should not be used on new drivers. + */ struct dvb_frontend_ops { struct dvb_frontend_info info; @@ -279,7 +435,8 @@ struct dvb_frontend_ops { bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status); + enum fe_status *status); + /* get frontend tuning algorithm from the module */ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe); @@ -289,7 +446,7 @@ struct dvb_frontend_ops { int (*get_frontend)(struct dvb_frontend *fe); - int (*read_status)(struct dvb_frontend* fe, fe_status_t* status); + int (*read_status)(struct dvb_frontend *fe, enum fe_status *status); int (*read_ber)(struct dvb_frontend* fe, u32* ber); int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength); int (*read_snr)(struct dvb_frontend* fe, u16* snr); @@ -298,9 +455,11 @@ struct dvb_frontend_ops { int (*diseqc_reset_overload)(struct dvb_frontend* fe); int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply); - int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); - int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); - int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + int (*diseqc_send_burst)(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd); + int (*set_tone)(struct dvb_frontend *fe, enum fe_sec_tone_mode tone); + int (*set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); @@ -322,6 +481,7 @@ struct dvb_frontend_ops { #ifdef __DVB_CORE__ #define MAX_EVENT 8 +/* Used only internally at dvb_frontend.c */ struct dvb_fe_events { struct dvb_frontend_event events[MAX_EVENT]; int eventw; @@ -332,30 +492,100 @@ struct dvb_fe_events { }; #endif +/** + * struct dtv_frontend_properties - contains a list of properties that are + * specific to a digital TV standard. + * + * @frequency: frequency in Hz for terrestrial/cable or in kHz for + * Satellite + * @modulation: Frontend modulation type + * @voltage: SEC voltage (only Satellite) + * @sectone: SEC tone mode (only Satellite) + * @inversion: Spectral inversion + * @fec_inner: Forward error correction inner Code Rate + * @transmission_mode: Transmission Mode + * @bandwidth_hz: Bandwidth, in Hz. A zero value means that userspace + * wants to autodetect. + * @guard_interval: Guard Interval + * @hierarchy: Hierarchy + * @symbol_rate: Symbol Rate + * @code_rate_HP: high priority stream code rate + * @code_rate_LP: low priority stream code rate + * @pilot: Enable/disable/autodetect pilot tones + * @rolloff: Rolloff factor (alpha) + * @delivery_system: FE delivery system (e. g. digital TV standard) + * @interleaving: interleaving + * @isdbt_partial_reception: ISDB-T partial reception (only ISDB standard) + * @isdbt_sb_mode: ISDB-T Sound Broadcast (SB) mode (only ISDB standard) + * @isdbt_sb_subchannel: ISDB-T SB subchannel (only ISDB standard) + * @isdbt_sb_segment_idx: ISDB-T SB segment index (only ISDB standard) + * @isdbt_sb_segment_count: ISDB-T SB segment count (only ISDB standard) + * @isdbt_layer_enabled: ISDB Layer enabled (only ISDB standard) + * @layer: ISDB per-layer data (only ISDB standard) + * @layer.segment_count: Segment Count; + * @layer.fec: per layer code rate; + * @layer.modulation: per layer modulation; + * @layer.interleaving: per layer interleaving. + * @stream_id: If different than zero, enable substream filtering, if + * hardware supports (DVB-S2 and DVB-T2). + * @atscmh_fic_ver: Version number of the FIC (Fast Information Channel) + * signaling data (only ATSC-M/H) + * @atscmh_parade_id: Parade identification number (only ATSC-M/H) + * @atscmh_nog: Number of MH groups per MH subframe for a designated + * parade (only ATSC-M/H) + * @atscmh_tnog: Total number of MH groups including all MH groups + * belonging to all MH parades in one MH subframe + * (only ATSC-M/H) + * @atscmh_sgn: Start group number (only ATSC-M/H) + * @atscmh_prc: Parade repetition cycle (only ATSC-M/H) + * @atscmh_rs_frame_mode: Reed Solomon (RS) frame mode (only ATSC-M/H) + * @atscmh_rs_frame_ensemble: RS frame ensemble (only ATSC-M/H) + * @atscmh_rs_code_mode_pri: RS code mode pri (only ATSC-M/H) + * @atscmh_rs_code_mode_sec: RS code mode sec (only ATSC-M/H) + * @atscmh_sccc_block_mode: Series Concatenated Convolutional Code (SCCC) + * Block Mode (only ATSC-M/H) + * @atscmh_sccc_code_mode_a: SCCC code mode A (only ATSC-M/H) + * @atscmh_sccc_code_mode_b: SCCC code mode B (only ATSC-M/H) + * @atscmh_sccc_code_mode_c: SCCC code mode C (only ATSC-M/H) + * @atscmh_sccc_code_mode_d: SCCC code mode D (only ATSC-M/H) + * @lna: Power ON/OFF/AUTO the Linear Now-noise Amplifier (LNA) + * @strength: DVBv5 API statistics: Signal Strength + * @cnr: DVBv5 API statistics: Signal to Noise ratio of the + * (main) carrier + * @pre_bit_error: DVBv5 API statistics: pre-Viterbi bit error count + * @pre_bit_count: DVBv5 API statistics: pre-Viterbi bit count + * @post_bit_error: DVBv5 API statistics: post-Viterbi bit error count + * @post_bit_count: DVBv5 API statistics: post-Viterbi bit count + * @block_error: DVBv5 API statistics: block error count + * @block_count: DVBv5 API statistics: block count + * + * NOTE: derivated statistics like Uncorrected Error blocks (UCE) are + * calculated on userspace. + * + * Only a subset of the properties are needed for a given delivery system. + * For more info, consult the media_api.html with the documentation of the + * Userspace API. + */ struct dtv_frontend_properties { - - /* Cache State */ - u32 state; - u32 frequency; - fe_modulation_t modulation; + enum fe_modulation modulation; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t sectone; - fe_spectral_inversion_t inversion; - fe_code_rate_t fec_inner; - fe_transmit_mode_t transmission_mode; + enum fe_sec_voltage voltage; + enum fe_sec_tone_mode sectone; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec_inner; + enum fe_transmit_mode transmission_mode; u32 bandwidth_hz; /* 0 = AUTO */ - fe_guard_interval_t guard_interval; - fe_hierarchy_t hierarchy; + enum fe_guard_interval guard_interval; + enum fe_hierarchy hierarchy; u32 symbol_rate; - fe_code_rate_t code_rate_HP; - fe_code_rate_t code_rate_LP; + enum fe_code_rate code_rate_HP; + enum fe_code_rate code_rate_LP; - fe_pilot_t pilot; - fe_rolloff_t rolloff; + enum fe_pilot pilot; + enum fe_rolloff rolloff; - fe_delivery_system_t delivery_system; + enum fe_delivery_system delivery_system; enum fe_interleaving interleaving; @@ -368,8 +598,8 @@ struct dtv_frontend_properties { u8 isdbt_layer_enabled; struct { u8 segment_count; - fe_code_rate_t fec; - fe_modulation_t modulation; + enum fe_code_rate fec; + enum fe_modulation modulation; u8 interleaving; } layer[3]; @@ -405,6 +635,11 @@ struct dtv_frontend_properties { struct dtv_fe_stats post_bit_count; struct dtv_fe_stats block_error; struct dtv_fe_stats block_count; + + /* private: */ + /* Cache State */ + u32 state; + }; #define DVB_FE_NO_EXIT 0 @@ -412,6 +647,25 @@ struct dtv_frontend_properties { #define DVB_FE_DEVICE_REMOVED 2 #define DVB_FE_DEVICE_RESUME 3 +/** + * struct dvb_frontend - Frontend structure to be used on drivers. + * + * @ops: embedded struct dvb_frontend_ops + * @dvb: pointer to struct dvb_adapter + * @demodulator_priv: demod private data + * @tuner_priv: tuner private data + * @frontend_priv: frontend private data + * @sec_priv: SEC private data + * @analog_demod_priv: Analog demod private data + * @dtv_property_cache: embedded struct dtv_frontend_properties + * @callback: callback function used on some drivers to call + * either the tuner or the demodulator. + * @id: Frontend ID + * @exit: Used to inform the DVB core that the frontend + * thread should exit (usually, means that the hardware + * got disconnected. + */ + struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; @@ -439,7 +693,6 @@ extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); extern int dvb_frontend_suspend(struct dvb_frontend *fe); extern int dvb_frontend_resume(struct dvb_frontend *fe); -extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); -extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); +extern void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec); #endif diff --git a/kernel/drivers/media/dvb-core/dvb_math.h b/kernel/drivers/media/dvb-core/dvb_math.h index aecc867e9..34dc1df03 100644 --- a/kernel/drivers/media/dvb-core/dvb_math.h +++ b/kernel/drivers/media/dvb-core/dvb_math.h @@ -25,33 +25,38 @@ #include /** - * computes log2 of a value; the result is shifted left by 24 bits + * cintlog2 - computes log2 of a value; the result is shifted left by 24 bits + * + * @value: The value (must be != 0) * * to use rational values you can use the following method: * intlog2(value) = intlog2(value * 2^x) - x * 2^24 * - * example: intlog2(8) will give 3 << 24 = 3 * 2^24 - * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 - * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 + * Some usecase examples: + * intlog2(8) will give 3 << 24 = 3 * 2^24 + * intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 + * intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 + * * - * @param value The value (must be != 0) - * @return log2(value) * 2^24 + * return: log2(value) * 2^24 */ extern unsigned int intlog2(u32 value); /** - * computes log10 of a value; the result is shifted left by 24 bits + * intlog10 - computes log10 of a value; the result is shifted left by 24 bits + * + * @value: The value (must be != 0) * * to use rational values you can use the following method: * intlog10(value) = intlog10(value * 10^x) - x * 2^24 * - * example: intlog10(1000) will give 3 << 24 = 3 * 2^24 + * An usecase example: + * intlog10(1000) will give 3 << 24 = 3 * 2^24 * due to the implementation intlog10(1000) might be not exactly 3 * 2^24 * * look at intlog2 for similar examples * - * @param value The value (must be != 0) - * @return log10(value) * 2^24 + * return: log10(value) * 2^24 */ extern unsigned int intlog10(u32 value); diff --git a/kernel/drivers/media/dvb-core/dvb_net.c b/kernel/drivers/media/dvb-core/dvb_net.c index a694fb1ea..ce4332e80 100644 --- a/kernel/drivers/media/dvb-core/dvb_net.c +++ b/kernel/drivers/media/dvb-core/dvb_net.c @@ -709,7 +709,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (!priv->ule_dbit) { /* dest_addr buffer is only valid if priv->ule_dbit == 0 */ memcpy(ethh->h_dest, dest_addr, ETH_ALEN); - memset(ethh->h_source, 0, ETH_ALEN); + eth_zero_addr(ethh->h_source); } else /* zeroize source and dest */ memset( ethh, 0, ETH_ALEN*2 ); @@ -761,7 +761,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_ts_feed *feed, enum dmx_success success) + struct dmx_ts_feed *feed) { struct net_device *dev = feed->priv; @@ -870,8 +870,7 @@ static void dvb_net_sec(struct net_device *dev, static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, const u8 *buffer2, size_t buffer2_len, - struct dmx_section_filter *filter, - enum dmx_success success) + struct dmx_section_filter *filter) { struct net_device *dev = filter->priv; diff --git a/kernel/drivers/media/dvb-core/dvb_ringbuffer.h b/kernel/drivers/media/dvb-core/dvb_ringbuffer.h index 9e1e11b7c..3ebc2d34b 100644 --- a/kernel/drivers/media/dvb-core/dvb_ringbuffer.h +++ b/kernel/drivers/media/dvb-core/dvb_ringbuffer.h @@ -45,33 +45,33 @@ struct dvb_ringbuffer { /* -** Notes: -** ------ -** (1) For performance reasons read and write routines don't check buffer sizes -** and/or number of bytes free/available. This has to be done before these -** routines are called. For example: -** -** *** write bytes *** -** free = dvb_ringbuffer_free(rbuf); -** if (free >= buflen) -** count = dvb_ringbuffer_write(rbuf, buffer, buflen); -** else -** ... -** -** *** read min. 1000, max. bytes *** -** avail = dvb_ringbuffer_avail(rbuf); -** if (avail >= 1000) -** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); -** else -** ... -** -** (2) If there is exactly one reader and one writer, there is no need -** to lock read or write operations. -** Two or more readers must be locked against each other. -** Flushing the buffer counts as a read operation. -** Resetting the buffer counts as a read and write operation. -** Two or more writers must be locked against each other. -*/ + * Notes: + * ------ + * (1) For performance reasons read and write routines don't check buffer sizes + * and/or number of bytes free/available. This has to be done before these + * routines are called. For example: + * + * *** write @buflen: bytes *** + * free = dvb_ringbuffer_free(rbuf); + * if (free >= buflen) + * count = dvb_ringbuffer_write(rbuf, buffer, buflen); + * else + * ... + * + * *** read min. 1000, max. @bufsize: bytes *** + * avail = dvb_ringbuffer_avail(rbuf); + * if (avail >= 1000) + * count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); + * else + * ... + * + * (2) If there is exactly one reader and one writer, there is no need + * to lock read or write operations. + * Two or more readers must be locked against each other. + * Flushing the buffer counts as a read operation. + * Resetting the buffer counts as a read and write operation. + * Two or more writers must be locked against each other. + */ /* initialize ring buffer, lock and queue */ extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len); @@ -87,9 +87,9 @@ extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); /* -** Reset the read and write pointers to zero and flush the buffer -** This counts as a read and write operation -*/ + * Reset the read and write pointers to zero and flush the buffer + * This counts as a read and write operation + */ extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); @@ -101,19 +101,19 @@ extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf); /* flush buffer protected by spinlock and wake-up waiting task(s) */ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); -/* peek at byte in the buffer */ +/* peek at byte @offs: in the buffer */ #define DVB_RINGBUFFER_PEEK(rbuf,offs) \ (rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size] -/* advance read ptr by bytes */ +/* advance read ptr by @num: bytes */ #define DVB_RINGBUFFER_SKIP(rbuf,num) \ (rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size /* -** read bytes from ring buffer into -** specifies whether resides in user space -** returns number of bytes transferred or -EFAULT -*/ + * read @len: bytes from ring buffer into @buf: + * @usermem: specifies whether @buf: resides in user space + * returns number of bytes transferred or -EFAULT + */ extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len); extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, @@ -127,9 +127,9 @@ extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, { (rbuf)->data[(rbuf)->pwrite]=(byte); \ (rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; } /* -** write bytes to ring buffer -** specifies whether resides in user space -** returns number of bytes transferred or -EFAULT + * write @len: bytes to ring buffer + * @usermem: specifies whether @buf: resides in user space + * returns number of bytes transferred or -EFAULT */ extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len); @@ -138,48 +138,63 @@ extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, /** - * Write a packet into the ringbuffer. + * dvb_ringbuffer_pkt_write - Write a packet into the ringbuffer. * - * Ringbuffer to write to. - * Buffer to write. - * Length of buffer (currently limited to 65535 bytes max). + * @rbuf: Ringbuffer to write to. + * @buf: Buffer to write. + * @len: Length of buffer (currently limited to 65535 bytes max). * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. */ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len); /** - * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this - * does NOT update the read pointer in the ringbuffer. You must use - * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required. - * - * Ringbuffer concerned. - * Packet index as returned by dvb_ringbuffer_pkt_next(). - * Offset into packet to read from. - * Destination buffer for data. - * Size of destination buffer. - * Set to 1 if is in userspace. + * dvb_ringbuffer_pkt_read_user - Read from a packet in the ringbuffer. + * Note: unlike dvb_ringbuffer_read(), this does NOT update the read pointer + * in the ringbuffer. You must use dvb_ringbuffer_pkt_dispose() to mark a + * packet as no longer required. + * + * @rbuf: Ringbuffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). + * @offset: Offset into packet to read from. + * @buf: Destination buffer for data. + * @len: Size of destination buffer. + * * returns Number of bytes read, or -EFAULT. */ extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, int offset, u8 __user *buf, size_t len); + +/** + * dvb_ringbuffer_pkt_read - Read from a packet in the ringbuffer. + * Note: unlike dvb_ringbuffer_read_user(), this DOES update the read pointer + * in the ringbuffer. + * + * @rbuf: Ringbuffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). + * @offset: Offset into packet to read from. + * @buf: Destination buffer for data. + * @len: Size of destination buffer. + * + * returns Number of bytes read, or -EFAULT. + */ extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, int offset, u8 *buf, size_t len); /** - * Dispose of a packet in the ring buffer. + * dvb_ringbuffer_pkt_dispose - Dispose of a packet in the ring buffer. * - * Ring buffer concerned. - * Packet index as returned by dvb_ringbuffer_pkt_next(). + * @rbuf: Ring buffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). */ extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx); /** - * Get the index of the next packet in a ringbuffer. + * dvb_ringbuffer_pkt_next - Get the index of the next packet in a ringbuffer. * - * Ringbuffer concerned. - * Previous packet index, or -1 to return the first packet index. - * On success, will be updated to contain the length of the packet in bytes. + * @rbuf: Ringbuffer concerned. + * @idx: Previous packet index, or -1 to return the first packet index. + * @pktlen: On success, will be updated to contain the length of the packet in bytes. * returns Packet index (if >=0), or -1 if no packets available. */ extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen); diff --git a/kernel/drivers/media/dvb-core/dvbdev.h b/kernel/drivers/media/dvb-core/dvbdev.h index 12629b8ec..1069a776b 100644 --- a/kernel/drivers/media/dvb-core/dvbdev.h +++ b/kernel/drivers/media/dvb-core/dvbdev.h @@ -57,6 +57,25 @@ struct dvb_frontend; +/** + * struct dvb_adapter - represents a Digital TV adapter using Linux DVB API + * + * @num: Number of the adapter + * @list_head: List with the DVB adapters + * @device_list: List with the DVB devices + * @name: Name of the adapter + * @proposed_mac: proposed MAC address for the adapter + * @priv: private data + * @device: pointer to struct device + * @module: pointer to struct module + * @mfe_shared: mfe shared: indicates mutually exclusive frontends + * Thie usage of this flag is currently deprecated + * @mfe_dvbdev: Frontend device in use, in the case of MFE + * @mfe_lock: Lock to prevent using the other frontends when MFE is + * used. + * @mdev: pointer to struct media_device, used when the media + * controller is used. + */ struct dvb_adapter { int num; struct list_head list_head; @@ -78,7 +97,34 @@ struct dvb_adapter { #endif }; - +/** + * struct dvb_device - represents a DVB device node + * + * @list_head: List head with all DVB devices + * @fops: pointer to struct file_operations + * @adapter: pointer to the adapter that holds this device node + * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, + * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + * @minor: devnode minor number. Major number is always DVB_MAJOR. + * @id: device ID number, inside the adapter + * @readers: Initialized by the caller. Each call to open() in Read Only mode + * decreases this counter by one. + * @writers: Initialized by the caller. Each call to open() in Read/Write + * mode decreases this counter by one. + * @users: Initialized by the caller. Each call to open() in any mode + * decreases this counter by one. + * @wait_queue: wait queue, used to wait for certain events inside one of + * the DVB API callers + * @kernel_ioctl: callback function used to handle ioctl calls from userspace. + * @name: Name to be used for the device at the Media Controller + * @entity: pointer to struct media_entity associated with the device node + * @pads: pointer to struct media_pad associated with @entity; + * @priv: private data + * + * This structure is used by the DVB core (frontend, CA, net, demux) in + * order to create the device nodes. Usually, driver should not initialize + * this struct diretly. + */ struct dvb_device { struct list_head list_head; const struct file_operations *fops; @@ -109,19 +155,51 @@ struct dvb_device { void *priv; }; +/** + * dvb_register_adapter - Registers a new DVB adapter + * + * @adap: pointer to struct dvb_adapter + * @name: Adapter's name + * @module: initialized with THIS_MODULE at the caller + * @device: pointer to struct device that corresponds to the device driver + * @adapter_nums: Array with a list of the numbers for @dvb_register_adapter; + * to select among them. Typically, initialized with: + * DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums) + */ +int dvb_register_adapter(struct dvb_adapter *adap, const char *name, + struct module *module, struct device *device, + short *adapter_nums); -extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name, - struct module *module, struct device *device, - short *adapter_nums); -extern int dvb_unregister_adapter (struct dvb_adapter *adap); - -extern int dvb_register_device (struct dvb_adapter *adap, - struct dvb_device **pdvbdev, - const struct dvb_device *template, - void *priv, - int type); +/** + * dvb_unregister_adapter - Unregisters a DVB adapter + * + * @adap: pointer to struct dvb_adapter + */ +int dvb_unregister_adapter(struct dvb_adapter *adap); -extern void dvb_unregister_device (struct dvb_device *dvbdev); +/** + * dvb_register_device - Registers a new DVB device + * + * @adap: pointer to struct dvb_adapter + * @pdvbdev: pointer to the place where the new struct dvb_device will be + * stored + * @template: Template used to create &pdvbdev; + * @priv: private data + * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, + * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + */ +int dvb_register_device(struct dvb_adapter *adap, + struct dvb_device **pdvbdev, + const struct dvb_device *template, + void *priv, + int type); + +/** + * dvb_unregister_device - Unregisters a DVB device + * + * @dvbdev: pointer to struct dvb_device + */ +void dvb_unregister_device(struct dvb_device *dvbdev); #ifdef CONFIG_MEDIA_CONTROLLER_DVB void dvb_create_media_graph(struct dvb_adapter *adap); @@ -136,17 +214,17 @@ static inline void dvb_create_media_graph(struct dvb_adapter *adap) {} #define dvb_register_media_controller(a, b) {} #endif -extern int dvb_generic_open (struct inode *inode, struct file *file); -extern int dvb_generic_release (struct inode *inode, struct file *file); -extern long dvb_generic_ioctl (struct file *file, +int dvb_generic_open (struct inode *inode, struct file *file); +int dvb_generic_release (struct inode *inode, struct file *file); +long dvb_generic_ioctl (struct file *file, unsigned int cmd, unsigned long arg); /* we don't mess with video_usercopy() any more, we simply define out own dvb_usercopy(), which will hopefully become generic_usercopy() someday... */ -extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, - int (*func)(struct file *file, unsigned int cmd, void *arg)); +int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + int (*func)(struct file *file, unsigned int cmd, void *arg)); /** generic DVB attach function. */ #ifdef CONFIG_MEDIA_ATTACH diff --git a/kernel/drivers/media/dvb-frontends/Kconfig b/kernel/drivers/media/dvb-frontends/Kconfig index 97c151d5b..292c9479b 100644 --- a/kernel/drivers/media/dvb-frontends/Kconfig +++ b/kernel/drivers/media/dvb-frontends/Kconfig @@ -1,5 +1,5 @@ menu "Customise DVB Frontends" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST comment "Multistandard (satellite) frontends" depends on DVB_CORE @@ -36,8 +36,9 @@ config DVB_STV6110x A Silicon tuner that supports DVB-S and DVB-S2 modes config DVB_M88DS3103 - tristate "Montage M88DS3103" + tristate "Montage Technology M88DS3103" depends on DVB_CORE && I2C && I2C_MUX + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -223,6 +224,13 @@ config DVB_CX24117 help A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. +config DVB_CX24120 + tristate "Conexant CX24120 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + config DVB_SI21XX tristate "Silicon Labs SI21XX based" depends on DVB_CORE && I2C @@ -233,6 +241,7 @@ config DVB_SI21XX config DVB_TS2020 tristate "Montage Tehnology TS2020 based tuners" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. @@ -255,6 +264,7 @@ config DVB_MB86A16 config DVB_TDA10071 tristate "NXP TDA10071" depends on DVB_CORE && I2C + select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -441,6 +451,13 @@ config DVB_CXD2820R help Say Y when you want to support this frontend. +config DVB_CXD2841ER + tristate "Sony CXD2841ER" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" depends on DVB_CORE && I2C && I2C_MUX @@ -703,6 +720,14 @@ comment "SEC control devices for DVB-S" source "drivers/media/dvb-frontends/drx39xyj/Kconfig" +config DVB_LNBH25 + tristate "LNBH25 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chip. + Say Y when you want to support this chip. + config DVB_LNBP21 tristate "LNBP21/LNBH24 SEC controllers" depends on DVB_CORE && I2C @@ -806,6 +831,20 @@ config DVB_AF9033 depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT +config DVB_HORUS3A + tristate "Sony Horus3A tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + +config DVB_ASCOT2E + tristate "Sony Ascot2E tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/kernel/drivers/media/dvb-frontends/Makefile b/kernel/drivers/media/dvb-frontends/Makefile index 23d399bec..37ef17b5b 100644 --- a/kernel/drivers/media/dvb-frontends/Makefile +++ b/kernel/drivers/media/dvb-frontends/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_LNBH25) += lnbh25.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_LNBP22) += lnbp22.o obj-$(CONFIG_DVB_ISL6405) += isl6405.o @@ -83,6 +84,7 @@ obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o obj-$(CONFIG_DVB_AF9013) += af9013.o obj-$(CONFIG_DVB_CX24116) += cx24116.o obj-$(CONFIG_DVB_CX24117) += cx24117.o +obj-$(CONFIG_DVB_CX24120) += cx24120.o obj-$(CONFIG_DVB_SI21XX) += si21xx.o obj-$(CONFIG_DVB_SI2168) += si2168.o obj-$(CONFIG_DVB_STV0288) += stv0288.o @@ -104,6 +106,7 @@ obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o obj-$(CONFIG_DVB_STV0367) += stv0367.o obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o +obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_SI2165) += si2165.o @@ -117,3 +120,5 @@ obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o +obj-$(CONFIG_DVB_HORUS3A) += horus3a.o +obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o diff --git a/kernel/drivers/media/dvb-frontends/a8293.c b/kernel/drivers/media/dvb-frontends/a8293.c index 780da5813..e1e9bddcf 100644 --- a/kernel/drivers/media/dvb-frontends/a8293.c +++ b/kernel/drivers/media/dvb-frontends/a8293.c @@ -12,157 +12,128 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "dvb_frontend.h" #include "a8293.h" -struct a8293_priv { - struct i2c_adapter *i2c; - const struct a8293_config *cfg; +struct a8293_dev { + struct i2c_client *client; u8 reg[2]; }; -static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) -{ - int ret; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_addr, - .len = len, - .buf = val, - } - }; - - if (rd) - msg[0].flags = I2C_M_RD; - else - msg[0].flags = 0; - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n", - KBUILD_MODNAME, ret, rd); - ret = -EREMOTEIO; - } - - return ret; -} - -static int a8293_wr(struct a8293_priv *priv, u8 *val, int len) -{ - return a8293_i2c(priv, val, len, 0); -} - -static int a8293_rd(struct a8293_priv *priv, u8 *val, int len) -{ - return a8293_i2c(priv, val, len, 1); -} - static int a8293_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t fe_sec_voltage) + enum fe_sec_voltage fe_sec_voltage) { - struct a8293_priv *priv = fe->sec_priv; + struct a8293_dev *dev = fe->sec_priv; + struct i2c_client *client = dev->client; int ret; + u8 reg0, reg1; - dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, - fe_sec_voltage); + dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_OFF: /* ENB=0 */ - priv->reg[0] = 0x10; + reg0 = 0x10; break; case SEC_VOLTAGE_13: /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ - priv->reg[0] = 0x31; + reg0 = 0x31; break; case SEC_VOLTAGE_18: /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ - priv->reg[0] = 0x38; + reg0 = 0x38; break; default: ret = -EINVAL; goto err; } + if (reg0 != dev->reg[0]) { + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + } - ret = a8293_wr(priv, &priv->reg[0], 1); - if (ret) - goto err; + /* TMODE=0, TGATE=1 */ + reg1 = 0x82; + if (reg1 != dev->reg[1]) { + ret = i2c_master_send(client, ®1, 1); + if (ret < 0) + goto err; + dev->reg[1] = reg1; + } usleep_range(1500, 50000); - - return ret; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static void a8293_release_sec(struct dvb_frontend *fe) -{ - a8293_set_voltage(fe, SEC_VOLTAGE_OFF); - - kfree(fe->sec_priv); - fe->sec_priv = NULL; -} - -struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg) +static int a8293_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct a8293_dev *dev; + struct a8293_platform_data *pdata = client->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; int ret; - struct a8293_priv *priv = NULL; u8 buf[2]; - /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL); - if (priv == NULL) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; goto err; } - /* setup the priv */ - priv->i2c = i2c; - priv->cfg = cfg; - fe->sec_priv = priv; + dev->client = client; /* check if the SEC is there */ - ret = a8293_rd(priv, buf, 2); - if (ret) - goto err; - - /* ENB=0 */ - priv->reg[0] = 0x10; - ret = a8293_wr(priv, &priv->reg[0], 1); - if (ret) - goto err; - - /* TMODE=0, TGATE=1 */ - priv->reg[1] = 0x82; - ret = a8293_wr(priv, &priv->reg[1], 1); - if (ret) - goto err; - - fe->ops.release_sec = a8293_release_sec; + ret = i2c_master_recv(client, buf, 2); + if (ret < 0) + goto err_kfree; /* override frontend ops */ fe->ops.set_voltage = a8293_set_voltage; + fe->sec_priv = dev; + i2c_set_clientdata(client, dev); - dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n", - KBUILD_MODNAME); - - return fe; + dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n"); + return 0; +err_kfree: + kfree(dev); err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int a8293_remove(struct i2c_client *client) +{ + struct a8293_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(dev); + return 0; } -EXPORT_SYMBOL(a8293_attach); + +static const struct i2c_device_id a8293_id_table[] = { + {"a8293", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, a8293_id_table); + +static struct i2c_driver a8293_driver = { + .driver = { + .name = "a8293", + .suppress_bind_attrs = true, + }, + .probe = a8293_probe, + .remove = a8293_remove, + .id_table = a8293_id_table, +}; + +module_i2c_driver(a8293_driver); MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("Allegro A8293 SEC driver"); diff --git a/kernel/drivers/media/dvb-frontends/a8293.h b/kernel/drivers/media/dvb-frontends/a8293.h index 5f0411939..7b90a03fc 100644 --- a/kernel/drivers/media/dvb-frontends/a8293.h +++ b/kernel/drivers/media/dvb-frontends/a8293.h @@ -12,31 +12,24 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef A8293_H #define A8293_H -#include +#include "dvb_frontend.h" -struct a8293_config { - u8 i2c_addr; -}; +/* + * I2C address + * 0x08, 0x09, 0x0a, 0x0b + */ -#if IS_REACHABLE(CONFIG_DVB_A8293) -extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg); -#else -static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif +/** + * struct a8293_platform_data - Platform data for the a8293 driver + * @dvb_frontend: DVB frontend. + */ +struct a8293_platform_data { + struct dvb_frontend *dvb_frontend; +}; #endif /* A8293_H */ diff --git a/kernel/drivers/media/dvb-frontends/af9013.c b/kernel/drivers/media/dvb-frontends/af9013.c index ba6c8f6c4..e23197da8 100644 --- a/kernel/drivers/media/dvb-frontends/af9013.c +++ b/kernel/drivers/media/dvb-frontends/af9013.c @@ -39,7 +39,7 @@ struct af9013_state { u32 ucblocks; u16 snr; u32 bandwidth_hz; - fe_status_t fe_status; + enum fe_status fe_status; unsigned long set_frontend_jiffies; unsigned long read_status_jiffies; bool first_tune; @@ -983,7 +983,7 @@ err: return ret; } -static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct af9013_state *state = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/af9033.c b/kernel/drivers/media/dvb-frontends/af9033.c index 82ce47bdf..bc35206a0 100644 --- a/kernel/drivers/media/dvb-frontends/af9033.c +++ b/kernel/drivers/media/dvb-frontends/af9033.c @@ -35,7 +35,7 @@ struct af9033_dev { bool ts_mode_parallel; bool ts_mode_serial; - fe_status_t fe_status; + enum fe_status fe_status; u64 post_bit_error_prev; /* for old read_ber we return (curr - prev) */ u64 post_bit_error; u64 post_bit_count; @@ -818,7 +818,7 @@ err: return ret; } -static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct af9033_dev *dev = fe->demodulator_priv; int ret; @@ -1387,7 +1387,6 @@ MODULE_DEVICE_TABLE(i2c, af9033_id_table); static struct i2c_driver af9033_driver = { .driver = { - .owner = THIS_MODULE, .name = "af9033", }, .probe = af9033_probe, diff --git a/kernel/drivers/media/dvb-frontends/as102_fe.c b/kernel/drivers/media/dvb-frontends/as102_fe.c index 493665899..544c5f65d 100644 --- a/kernel/drivers/media/dvb-frontends/as102_fe.c +++ b/kernel/drivers/media/dvb-frontends/as102_fe.c @@ -32,7 +32,7 @@ struct as102_state { uint32_t ber; }; -static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg) +static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg) { uint8_t c; @@ -306,7 +306,7 @@ static int as102_fe_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int as102_fe_read_status(struct dvb_frontend *fe, enum fe_status *status) { int ret = 0; struct as102_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/ascot2e.c b/kernel/drivers/media/dvb-frontends/ascot2e.c new file mode 100644 index 000000000..f770f6a2c --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/ascot2e.c @@ -0,0 +1,548 @@ +/* + * ascot2e.c + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include "ascot2e.h" +#include "dvb_frontend.h" + +#define MAX_WRITE_REGSIZE 10 + +enum ascot2e_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct ascot2e_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum ascot2e_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +enum ascot2e_tv_system_t { + ASCOT2E_DTV_DVBT_5, + ASCOT2E_DTV_DVBT_6, + ASCOT2E_DTV_DVBT_7, + ASCOT2E_DTV_DVBT_8, + ASCOT2E_DTV_DVBT2_1_7, + ASCOT2E_DTV_DVBT2_5, + ASCOT2E_DTV_DVBT2_6, + ASCOT2E_DTV_DVBT2_7, + ASCOT2E_DTV_DVBT2_8, + ASCOT2E_DTV_DVBC_6, + ASCOT2E_DTV_DVBC_8, + ASCOT2E_DTV_DVBC2_6, + ASCOT2E_DTV_DVBC2_8, + ASCOT2E_DTV_UNKNOWN +}; + +struct ascot2e_band_sett { + u8 if_out_sel; + u8 agc_sel; + u8 mix_oll; + u8 rf_gain; + u8 if_bpf_gc; + u8 fif_offset; + u8 bw_offset; + u8 bw; + u8 rf_oldet; + u8 if_bpf_f0; +}; + +#define ASCOT2E_AUTO 0xff +#define ASCOT2E_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define ASCOT2E_BW_6 0x00 +#define ASCOT2E_BW_7 0x01 +#define ASCOT2E_BW_8 0x02 +#define ASCOT2E_BW_1_7 0x03 + +static struct ascot2e_band_sett ascot2e_sett[] = { + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2), ASCOT2E_BW_8, 0x09, 0x00 } +}; + +static void ascot2e_i2c_debug(struct ascot2e_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("ascot2e: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int ascot2e_write_regs(struct ascot2e_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + ascot2e_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val) +{ + return ascot2e_write_regs(priv, reg, &val, 1); +} + +static int ascot2e_read_regs(struct ascot2e_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ascot2e_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val) +{ + return ascot2e_read_regs(priv, reg, val, 1); +} + +static int ascot2e_set_reg_bits(struct ascot2e_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = ascot2e_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return ascot2e_write_reg(priv, reg, data); +} + +static int ascot2e_enter_power_save(struct ascot2e_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + return 0; +} + +static int ascot2e_leave_power_save(struct ascot2e_priv *priv) +{ + u8 data[2] = { 0xFB, 0x0F }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x00); + priv->state = STATE_ACTIVE; + return 0; +} + +static int ascot2e_init(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return ascot2e_leave_power_save(priv); +} + +static int ascot2e_release(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int ascot2e_sleep(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + ascot2e_enter_power_save(priv); + return 0; +} + +static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe) +{ + enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT_8; + else { + system = ASCOT2E_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT2_8; + else { + system = ASCOT2E_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, p->bandwidth_hz); + return system; +} + +static int ascot2e_set_params(struct dvb_frontend *fe) +{ + u8 data[10]; + u32 frequency; + enum ascot2e_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, p->frequency / 1000); + tv_system = ascot2e_get_tv_system(fe); + + if (tv_system == ASCOT2E_DTV_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + if (priv->state == STATE_SLEEP) + ascot2e_leave_power_save(priv); + + /* IF_OUT_SEL / AGC_SEL setting */ + data[0] = 0x00; + if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) { + /* AGC pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].agc_sel & 0x03) << 3); + } + if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) { + /* IFOUT pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].if_out_sel & 0x01) << 2); + } + /* Set bit[4:2] only */ + ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c); + /* 0x06 - 0x0F */ + /* REF_R setting (0x06) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + /* xtal, xtal*2 */ + data[0] = (frequency > 500000) ? 16 : 32; + } else { + /* xtal/8, xtal/4 */ + data[0] = (frequency > 500000) ? 2 : 4; + } + /* XOSC_SEL=100uA */ + data[1] = 0x04; + /* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[2] = 18; + data[3] = 120; + data[4] = 20; + } else { + data[2] = 48; + data[3] = 10; + data[4] = 30; + } + /* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) + data[5] = (frequency > 500000) ? 0x08 : 0x0c; + else + data[5] = (frequency > 500000) ? 0x30 : 0x38; + /* Set MIX_OLL (0x0C) value from parameter table */ + data[6] = ascot2e_sett[tv_system].mix_oll; + /* Set RF_GAIN (0x0D) setting from parameter table */ + if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) { + /* RF_GAIN auto control enable */ + ascot2e_write_reg(priv, 0x4E, 0x01); + /* RF_GAIN Default value */ + data[7] = 0x00; + } else { + /* RF_GAIN auto control disable */ + ascot2e_write_reg(priv, 0x4E, 0x00); + data[7] = ascot2e_sett[tv_system].rf_gain; + } + /* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */ + data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) | + (ascot2e_sett[tv_system].if_bpf_gc & 0x07)); + /* Set BW_OFFSET (0x0F) value from parameter table */ + data[9] = ascot2e_sett[tv_system].bw_offset; + ascot2e_write_regs(priv, 0x06, data, 10); + /* + * 0x45 - 0x47 + * LNA optimization setting + * RF_LNA_DIST1-5, RF_LNA_CM + */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x01; + } else { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x03; + } + ascot2e_write_regs(priv, 0x45, data, 3); + /* 0x49 - 0x4A + Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */ + data[0] = ascot2e_sett[tv_system].rf_oldet; + /* Set IF_BPF_F0 value from parameter table */ + data[1] = ascot2e_sett[tv_system].if_bpf_f0; + ascot2e_write_regs(priv, 0x49, data, 2); + /* + * Tune now + * RFAGC fast mode / RFAGC auto control enable + * (set bit[7], bit[5:4] only) + * vco_cal = 1, set MIX_OL_CPU_EN + */ + ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0); + /* Logic wake up, CPU wake up */ + data[0] = 0xc4; + data[1] = 0x40; + ascot2e_write_regs(priv, 0x03, data, 2); + /* 0x10 - 0x14 */ + data[0] = (u8)(frequency & 0xFF); /* 0x10: FRF_L */ + data[1] = (u8)((frequency >> 8) & 0xFF); /* 0x11: FRF_M */ + data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */ + /* 0x12: BW (bit[5:4]) */ + data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4); + data[3] = 0xFF; /* 0x13: VCO calibration enable */ + data[4] = 0xFF; /* 0x14: Analog block enable */ + /* Tune (Burst write) */ + ascot2e_write_regs(priv, 0x10, data, 5); + msleep(50); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xC0); + /* RFAGC normal mode (set bit[5:4] only) */ + ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30); + priv->frequency = frequency; + return 0; +} + +static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static struct dvb_tuner_ops ascot2e_tuner_ops = { + .info = { + .name = "Sony ASCOT2E", + .frequency_min = 1000000, + .frequency_max = 1200000000, + .frequency_step = 25000, + }, + .init = ascot2e_init, + .release = ascot2e_release, + .sleep = ascot2e_sleep, + .set_params = ascot2e_set_params, + .get_frequency = ascot2e_get_frequency, +}; + +struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + u8 data[4]; + struct ascot2e_priv *priv = NULL; + + priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* 16 MHz xTal frequency */ + data[0] = 16; + /* VCO current setting */ + data[1] = 0x06; + /* Logic wake up, CPU boot */ + data[2] = 0xC4; + data[3] = 0x40; + ascot2e_write_regs(priv, 0x01, data, 4); + /* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */ + data[0] = 0x10; + data[1] = 0x3F; + data[2] = 0x25; + ascot2e_write_regs(priv, 0x22, data, 3); + /* PLL mode setting */ + ascot2e_write_reg(priv, 0x28, 0x1e); + /* RSSI setting */ + ascot2e_write_reg(priv, 0x59, 0x04); + /* TODO check CPU HW error state here */ + msleep(80); + /* Xtal oscillator current control setting */ + ascot2e_write_reg(priv, 0x4c, 0x01); + /* XOSC_SEL=100uA */ + ascot2e_write_reg(priv, 0x07, 0x04); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xc0); + /* Power save setting */ + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony ASCOT2E attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(ascot2e_attach); + +MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/ascot2e.h b/kernel/drivers/media/dvb-frontends/ascot2e.h new file mode 100644 index 000000000..6da4ae6d6 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/ascot2e.h @@ -0,0 +1,58 @@ +/* + * ascot2e.h + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 __DVB_ASCOT2E_H__ +#define __DVB_ASCOT2E_H__ + +#include +#include +#include + +/** + * struct ascot2e_config - the configuration of Ascot2E tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct ascot2e_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_ASCOT2E) +extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/kernel/drivers/media/dvb-frontends/atbm8830.c b/kernel/drivers/media/dvb-frontends/atbm8830.c index 4e11dc4b1..8fe552e29 100644 --- a/kernel/drivers/media/dvb-frontends/atbm8830.c +++ b/kernel/drivers/media/dvb-frontends/atbm8830.c @@ -335,7 +335,8 @@ static int atbm8830_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +static int atbm8830_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) { struct atbm_state *priv = fe->demodulator_priv; u8 locked = 0; diff --git a/kernel/drivers/media/dvb-frontends/au8522_decoder.c b/kernel/drivers/media/dvb-frontends/au8522_decoder.c index 33aa9410b..28d7dc2fe 100644 --- a/kernel/drivers/media/dvb-frontends/au8522_decoder.c +++ b/kernel/drivers/media/dvb-frontends/au8522_decoder.c @@ -820,7 +820,6 @@ MODULE_DEVICE_TABLE(i2c, au8522_id); static struct i2c_driver au8522_driver = { .driver = { - .owner = THIS_MODULE, .name = "au8522", }, .probe = au8522_probe, diff --git a/kernel/drivers/media/dvb-frontends/au8522_dig.c b/kernel/drivers/media/dvb-frontends/au8522_dig.c index 5d06c99b0..f956f13fb 100644 --- a/kernel/drivers/media/dvb-frontends/au8522_dig.c +++ b/kernel/drivers/media/dvb-frontends/au8522_dig.c @@ -552,7 +552,7 @@ static struct { }; static int au8522_enable_modulation(struct dvb_frontend *fe, - fe_modulation_t m) + enum fe_modulation m) { struct au8522_state *state = fe->demodulator_priv; int i; @@ -644,7 +644,7 @@ static int au8522_set_frontend(struct dvb_frontend *fe) return 0; } -static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct au8522_state *state = fe->demodulator_priv; u8 reg; @@ -922,7 +922,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Enable verbose debug messages"); module_param(zv_mode, int, 0644); -MODULE_PARM_DESC(zv_mode, "Turn on/off ZeeVee modulator compatability mode (default:on).\n" +MODULE_PARM_DESC(zv_mode, "Turn on/off ZeeVee modulator compatibility mode (default:on).\n" "\t\ton - modified AU8522 QAM256 initialization.\n" "\t\tProvides faster lock when using ZeeVee modulator based sources"); diff --git a/kernel/drivers/media/dvb-frontends/au8522_priv.h b/kernel/drivers/media/dvb-frontends/au8522_priv.h index b8aca1c84..951b3847e 100644 --- a/kernel/drivers/media/dvb-frontends/au8522_priv.h +++ b/kernel/drivers/media/dvb-frontends/au8522_priv.h @@ -55,7 +55,7 @@ struct au8522_state { struct dvb_frontend frontend; u32 current_frequency; - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 fe_status; unsigned int led_state; diff --git a/kernel/drivers/media/dvb-frontends/bcm3510.c b/kernel/drivers/media/dvb-frontends/bcm3510.c index 638c7aa0f..d30275f27 100644 --- a/kernel/drivers/media/dvb-frontends/bcm3510.c +++ b/kernel/drivers/media/dvb-frontends/bcm3510.c @@ -289,7 +289,7 @@ static int bcm3510_refresh_state(struct bcm3510_state *st) return 0; } -static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int bcm3510_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct bcm3510_state* st = fe->demodulator_priv; bcm3510_refresh_state(st); @@ -685,7 +685,7 @@ static int bcm3510_reset(struct bcm3510_state *st) if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) return ret; - t = jiffies + 3*HZ; + t = jiffies + 3*HZ; while (time_before(jiffies, t)) { msleep(10); if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) @@ -708,7 +708,7 @@ static int bcm3510_clear_reset(struct bcm3510_state *st) if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) return ret; - t = jiffies + 3*HZ; + t = jiffies + 3*HZ; while (time_before(jiffies, t)) { msleep(10); if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) diff --git a/kernel/drivers/media/dvb-frontends/cx22700.c b/kernel/drivers/media/dvb-frontends/cx22700.c index 86563260d..fd033cca6 100644 --- a/kernel/drivers/media/dvb-frontends/cx22700.c +++ b/kernel/drivers/media/dvb-frontends/cx22700.c @@ -191,9 +191,10 @@ static int cx22700_set_tps(struct cx22700_state *state, static int cx22700_get_tps(struct cx22700_state *state, struct dtv_frontend_properties *p) { - static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; - static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, - FEC_5_6, FEC_7_8 }; + static const enum fe_modulation qam_tab[3] = { QPSK, QAM_16, QAM_64 }; + static const enum fe_code_rate fec_tab[5] = { + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8 + }; u8 val; dprintk ("%s\n", __func__); @@ -253,7 +254,7 @@ static int cx22700_init (struct dvb_frontend* fe) return 0; } -static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cx22700_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/cx22702.c b/kernel/drivers/media/dvb-frontends/cx22702.c index edc8eafc5..d2d06dcd7 100644 --- a/kernel/drivers/media/dvb-frontends/cx22702.c +++ b/kernel/drivers/media/dvb-frontends/cx22702.c @@ -452,7 +452,7 @@ static int cx22702_init(struct dvb_frontend *fe) return 0; } -static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int cx22702_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cx22702_state *state = fe->demodulator_priv; u8 reg0A; diff --git a/kernel/drivers/media/dvb-frontends/cx24110.c b/kernel/drivers/media/dvb-frontends/cx24110.c index 7b510f2ae..cb36475e3 100644 --- a/kernel/drivers/media/dvb-frontends/cx24110.c +++ b/kernel/drivers/media/dvb-frontends/cx24110.c @@ -143,7 +143,8 @@ static int cx24110_readreg (struct cx24110_state* state, u8 reg) return b1[0]; } -static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion) +static int cx24110_set_inversion(struct cx24110_state *state, + enum fe_spectral_inversion inversion) { /* fixme (low): error handling */ @@ -177,7 +178,7 @@ static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inver return 0; } -static int cx24110_set_fec(struct cx24110_state* state, fe_code_rate_t fec) +static int cx24110_set_fec(struct cx24110_state *state, enum fe_code_rate fec) { static const int rate[FEC_AUTO] = {-1, 1, 2, 3, 5, 7, -1}; static const int g1[FEC_AUTO] = {-1, 0x01, 0x02, 0x05, 0x15, 0x45, -1}; @@ -220,7 +221,7 @@ static int cx24110_set_fec(struct cx24110_state* state, fe_code_rate_t fec) return 0; } -static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) +static enum fe_code_rate cx24110_get_fec(struct cx24110_state *state) { int i; @@ -365,7 +366,8 @@ static int cx24110_initfe(struct dvb_frontend* fe) return 0; } -static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int cx24110_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct cx24110_state *state = fe->demodulator_priv; @@ -379,7 +381,8 @@ static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag } } -static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +static int cx24110_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { int rv, bit; struct cx24110_state *state = fe->demodulator_priv; @@ -434,7 +437,8 @@ static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, return 0; } -static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int cx24110_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct cx24110_state *state = fe->demodulator_priv; @@ -574,7 +578,8 @@ static int cx24110_get_frontend(struct dvb_frontend *fe) return 0; } -static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int cx24110_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct cx24110_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/cx24116.c b/kernel/drivers/media/dvb-frontends/cx24116.c index 7bc68b355..8814f36d5 100644 --- a/kernel/drivers/media/dvb-frontends/cx24116.c +++ b/kernel/drivers/media/dvb-frontends/cx24116.c @@ -160,13 +160,13 @@ enum cmds { struct cx24116_tuning { u32 frequency; u32 symbol_rate; - fe_spectral_inversion_t inversion; - fe_code_rate_t fec; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; - fe_delivery_system_t delsys; - fe_modulation_t modulation; - fe_pilot_t pilot; - fe_rolloff_t rolloff; + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + enum fe_rolloff rolloff; /* Demod values */ u8 fec_val; @@ -285,7 +285,7 @@ static int cx24116_readreg(struct cx24116_state *state, u8 reg) } static int cx24116_set_inversion(struct cx24116_state *state, - fe_spectral_inversion_t inversion) + enum fe_spectral_inversion inversion) { dprintk("%s(%d)\n", __func__, inversion); @@ -373,9 +373,9 @@ static int cx24116_set_inversion(struct cx24116_state *state, * a scheme are support. Especially, no auto detect when in S2 mode. */ static struct cx24116_modfec { - fe_delivery_system_t delivery_system; - fe_modulation_t modulation; - fe_code_rate_t fec; + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; u8 mask; /* In DVBS mode this is used to autodetect */ u8 val; /* Passed to the firmware to indicate mode selection */ } CX24116_MODFEC_MODES[] = { @@ -415,7 +415,7 @@ static struct cx24116_modfec { }; static int cx24116_lookup_fecmod(struct cx24116_state *state, - fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) + enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) { int i, ret = -EOPNOTSUPP; @@ -434,7 +434,9 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state, } static int cx24116_set_fec(struct cx24116_state *state, - fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) + enum fe_delivery_system delsys, + enum fe_modulation mod, + enum fe_code_rate fec) { int ret = 0; @@ -683,7 +685,7 @@ static int cx24116_load_firmware(struct dvb_frontend *fe, return 0; } -static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cx24116_state *state = fe->demodulator_priv; @@ -844,7 +846,7 @@ static int cx24116_wait_for_lnb(struct dvb_frontend *fe) } static int cx24116_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx24116_cmd cmd; int ret; @@ -872,7 +874,7 @@ static int cx24116_set_voltage(struct dvb_frontend *fe, } static int cx24116_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t tone) + enum fe_sec_tone_mode tone) { struct cx24116_cmd cmd; int ret; @@ -1055,7 +1057,7 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, /* Send DiSEqC burst */ static int cx24116_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct cx24116_state *state = fe->demodulator_priv; int ret; @@ -1220,7 +1222,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe) struct cx24116_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; - fe_status_t tunerstat; + enum fe_status tunerstat; int i, status, ret, retune = 1; dprintk("%s()\n", __func__); @@ -1441,7 +1443,7 @@ tuned: /* Set/Reset B/W */ } static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, - unsigned int mode_flags, unsigned int *delay, fe_status_t *status) + unsigned int mode_flags, unsigned int *delay, enum fe_status *status) { /* * It is safe to discard "params" here, as the DVB core will sync diff --git a/kernel/drivers/media/dvb-frontends/cx24117.c b/kernel/drivers/media/dvb-frontends/cx24117.c index af6363573..5f77bc80a 100644 --- a/kernel/drivers/media/dvb-frontends/cx24117.c +++ b/kernel/drivers/media/dvb-frontends/cx24117.c @@ -171,13 +171,13 @@ static DEFINE_MUTEX(cx24117_list_mutex); struct cx24117_tuning { u32 frequency; u32 symbol_rate; - fe_spectral_inversion_t inversion; - fe_code_rate_t fec; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; - fe_delivery_system_t delsys; - fe_modulation_t modulation; - fe_pilot_t pilot; - fe_rolloff_t rolloff; + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + enum fe_rolloff rolloff; /* Demod values */ u8 fec_val; @@ -220,9 +220,9 @@ struct cx24117_state { /* modfec (modulation and FEC) lookup table */ /* Check cx24116.c for a detailed description of each field */ static struct cx24117_modfec { - fe_delivery_system_t delivery_system; - fe_modulation_t modulation; - fe_code_rate_t fec; + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; u8 mask; /* In DVBS mode this is used to autodetect */ u8 val; /* Passed to the firmware to indicate mode selection */ } cx24117_modfec_modes[] = { @@ -362,7 +362,7 @@ static int cx24117_readregN(struct cx24117_state *state, } static int cx24117_set_inversion(struct cx24117_state *state, - fe_spectral_inversion_t inversion) + enum fe_spectral_inversion inversion) { dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n", __func__, inversion, state->demod); @@ -387,7 +387,7 @@ static int cx24117_set_inversion(struct cx24117_state *state, } static int cx24117_lookup_fecmod(struct cx24117_state *state, - fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) + enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) { int i, ret = -EINVAL; @@ -408,7 +408,9 @@ static int cx24117_lookup_fecmod(struct cx24117_state *state, } static int cx24117_set_fec(struct cx24117_state *state, - fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) + enum fe_delivery_system delsys, + enum fe_modulation mod, + enum fe_code_rate fec) { int ret; @@ -737,7 +739,7 @@ error: return ret; } -static int cx24117_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int cx24117_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cx24117_state *state = fe->demodulator_priv; int lock; @@ -843,7 +845,7 @@ static int cx24117_read_snr(struct dvb_frontend *fe, u16 *snr) static int cx24117_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct cx24117_state *state = fe->demodulator_priv; - fe_delivery_system_t delsys = fe->dtv_property_cache.delivery_system; + enum fe_delivery_system delsys = fe->dtv_property_cache.delivery_system; int ret; u8 buf[2]; u8 reg = (state->demod == 0) ? @@ -904,7 +906,7 @@ static int cx24117_wait_for_lnb(struct dvb_frontend *fe) } static int cx24117_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx24117_state *state = fe->demodulator_priv; struct cx24117_cmd cmd; @@ -956,7 +958,7 @@ static int cx24117_set_voltage(struct dvb_frontend *fe, } static int cx24117_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t tone) + enum fe_sec_tone_mode tone) { struct cx24117_state *state = fe->demodulator_priv; struct cx24117_cmd cmd; @@ -1112,7 +1114,7 @@ static int cx24117_send_diseqc_msg(struct dvb_frontend *fe, /* Send DiSEqC burst */ static int cx24117_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct cx24117_state *state = fe->demodulator_priv; @@ -1306,7 +1308,7 @@ static int cx24117_set_frontend(struct dvb_frontend *fe) struct cx24117_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24117_cmd cmd; - fe_status_t tunerstat; + enum fe_status tunerstat; int i, status, ret, retune = 1; u8 reg_clkdiv, reg_ratediv; @@ -1537,7 +1539,7 @@ static int cx24117_set_frontend(struct dvb_frontend *fe) } static int cx24117_tune(struct dvb_frontend *fe, bool re_tune, - unsigned int mode_flags, unsigned int *delay, fe_status_t *status) + unsigned int mode_flags, unsigned int *delay, enum fe_status *status) { struct cx24117_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/cx24120.c b/kernel/drivers/media/dvb-frontends/cx24120.c new file mode 100644 index 000000000..3b0ef52bb --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/cx24120.c @@ -0,0 +1,1595 @@ +/* + Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2008 Patrick Boettcher + Copyright (C) 2009 Sergey Tyurin + Updated 2012 by Jannis Achstetter + Copyright (C) 2015 Jemma Denson + April 2015 + Refactored & simplified driver + Updated to work with delivery system supplied by DVBv5 + Add frequency, fec & pilot to get_frontend + + Cards supported: Technisat Skystar S2 + + 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 +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "cx24120.h" + +#define CX24120_SEARCH_RANGE_KHZ 5000 +#define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw" + +/* cx24120 i2c registers */ +#define CX24120_REG_CMD_START 0x00 /* write cmd_id */ +#define CX24120_REG_CMD_ARGS 0x01 /* write command arguments */ +#define CX24120_REG_CMD_END 0x1f /* write 0x01 for end */ + +#define CX24120_REG_MAILBOX 0x33 +#define CX24120_REG_FREQ3 0x34 /* frequency */ +#define CX24120_REG_FREQ2 0x35 +#define CX24120_REG_FREQ1 0x36 + +#define CX24120_REG_FECMODE 0x39 /* FEC status */ +#define CX24120_REG_STATUS 0x3a /* Tuner status */ +#define CX24120_REG_SIGSTR_H 0x3a /* Signal strength high */ +#define CX24120_REG_SIGSTR_L 0x3b /* Signal strength low byte */ +#define CX24120_REG_QUALITY_H 0x40 /* SNR high byte */ +#define CX24120_REG_QUALITY_L 0x41 /* SNR low byte */ + +#define CX24120_REG_BER_HH 0x47 /* BER high byte of high word */ +#define CX24120_REG_BER_HL 0x48 /* BER low byte of high word */ +#define CX24120_REG_BER_LH 0x49 /* BER high byte of low word */ +#define CX24120_REG_BER_LL 0x4a /* BER low byte of low word */ + +#define CX24120_REG_UCB_H 0x50 /* UCB high byte */ +#define CX24120_REG_UCB_L 0x51 /* UCB low byte */ + +#define CX24120_REG_CLKDIV 0xe6 +#define CX24120_REG_RATEDIV 0xf0 + +#define CX24120_REG_REVISION 0xff /* Chip revision (ro) */ + +/* Command messages */ +enum command_message_id { + CMD_VCO_SET = 0x10, /* cmd.len = 12; */ + CMD_TUNEREQUEST = 0x11, /* cmd.len = 15; */ + + CMD_MPEG_ONOFF = 0x13, /* cmd.len = 4; */ + CMD_MPEG_INIT = 0x14, /* cmd.len = 7; */ + CMD_BANDWIDTH = 0x15, /* cmd.len = 12; */ + CMD_CLOCK_READ = 0x16, /* read clock */ + CMD_CLOCK_SET = 0x17, /* cmd.len = 10; */ + + CMD_DISEQC_MSG1 = 0x20, /* cmd.len = 11; */ + CMD_DISEQC_MSG2 = 0x21, /* cmd.len = d->msg_len + 6; */ + CMD_SETVOLTAGE = 0x22, /* cmd.len = 2; */ + CMD_SETTONE = 0x23, /* cmd.len = 4; */ + CMD_DISEQC_BURST = 0x24, /* cmd.len not used !!! */ + + CMD_READ_SNR = 0x1a, /* Read signal strength */ + CMD_START_TUNER = 0x1b, /* ??? */ + + CMD_FWVERSION = 0x35, + + CMD_BER_CTRL = 0x3c, /* cmd.len = 0x03; */ +}; + +#define CX24120_MAX_CMD_LEN 30 + +/* pilot mask */ +#define CX24120_PILOT_OFF 0x00 +#define CX24120_PILOT_ON 0x40 +#define CX24120_PILOT_AUTO 0x80 + +/* signal status */ +#define CX24120_HAS_SIGNAL 0x01 +#define CX24120_HAS_CARRIER 0x02 +#define CX24120_HAS_VITERBI 0x04 +#define CX24120_HAS_LOCK 0x08 +#define CX24120_HAS_UNK1 0x10 +#define CX24120_HAS_UNK2 0x20 +#define CX24120_STATUS_MASK 0x0f +#define CX24120_SIGNAL_MASK 0xc0 + +/* ber window */ +#define CX24120_BER_WINDOW 16 +#define CX24120_BER_WSIZE ((1 << CX24120_BER_WINDOW) * 208 * 8) + +#define info(args...) pr_info("cx24120: " args) +#define err(args...) pr_err("cx24120: ### ERROR: " args) + +/* The Demod/Tuner can't easily provide these, we cache them */ +struct cx24120_tuning { + u32 frequency; + u32 symbol_rate; + enum fe_spectral_inversion inversion; + enum fe_code_rate fec; + + enum fe_delivery_system delsys; + enum fe_modulation modulation; + enum fe_pilot pilot; + + /* Demod values */ + u8 fec_val; + u8 fec_mask; + u8 clkdiv; + u8 ratediv; + u8 inversion_val; + u8 pilot_val; +}; + +/* Private state */ +struct cx24120_state { + struct i2c_adapter *i2c; + const struct cx24120_config *config; + struct dvb_frontend frontend; + + u8 cold_init; + u8 mpeg_enabled; + u8 need_clock_set; + + /* current and next tuning parameters */ + struct cx24120_tuning dcur; + struct cx24120_tuning dnxt; + + enum fe_status fe_status; + + /* dvbv5 stats calculations */ + u32 bitrate; + u32 berw_usecs; + u32 ber_prev; + u32 ucb_offset; + unsigned long ber_jiffies_stats; + unsigned long per_jiffies_stats; +}; + +/* Command message to firmware */ +struct cx24120_cmd { + u8 id; + u8 len; + u8 arg[CX24120_MAX_CMD_LEN]; +}; + +/* Read single register */ +static int cx24120_readreg(struct cx24120_state *state, u8 reg) +{ + int ret; + u8 buf = 0; + struct i2c_msg msg[] = { + { + .addr = state->config->i2c_addr, + .flags = 0, + .len = 1, + .buf = ® + }, { + .addr = state->config->i2c_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &buf + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret != 2) { + err("Read error: reg=0x%02x, ret=%i)\n", reg, ret); + return ret; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf); + + return buf; +} + +/* Write single register */ +static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->i2c_addr, + .flags = 0, + .buf = buf, + .len = 2 + }; + int ret; + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n", + ret, reg, data); + return ret; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data); + + return 0; +} + +/* Write multiple registers in chunks of i2c_wr_max-sized buffers */ +static int cx24120_writeregs(struct cx24120_state *state, + u8 reg, const u8 *values, u16 len, u8 incr) +{ + int ret; + u16 max = state->config->i2c_wr_max > 0 ? + state->config->i2c_wr_max : + len; + + struct i2c_msg msg = { + .addr = state->config->i2c_addr, + .flags = 0, + }; + + msg.buf = kmalloc(max + 1, GFP_KERNEL); + if (!msg.buf) + return -ENOMEM; + + while (len) { + msg.buf[0] = reg; + msg.len = len > max ? max : len; + memcpy(&msg.buf[1], values, msg.len); + + len -= msg.len; /* data length revers counter */ + values += msg.len; /* incr data pointer */ + + if (incr) + reg += msg.len; + msg.len++; /* don't forget the addr byte */ + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + err("i2c_write error(err == %i, 0x%02x)\n", ret, reg); + goto out; + } + + dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n", + reg, msg.len - 1, msg.buf + 1); + } + + ret = 0; + +out: + kfree(msg.buf); + return ret; +} + +static struct dvb_frontend_ops cx24120_ops; + +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c) +{ + struct cx24120_state *state; + int demod_rev; + + info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n"); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + err("Unable to allocate memory for cx24120_state\n"); + goto error; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + /* check if the demod is present and has proper type */ + demod_rev = cx24120_readreg(state, CX24120_REG_REVISION); + switch (demod_rev) { + case 0x07: + info("Demod cx24120 rev. 0x07 detected.\n"); + break; + case 0x05: + info("Demod cx24120 rev. 0x05 detected.\n"); + break; + default: + err("Unsupported demod revision: 0x%x detected.\n", demod_rev); + goto error; + } + + /* create dvb_frontend */ + state->cold_init = 0; + memcpy(&state->frontend.ops, &cx24120_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + info("Conexant cx24120/cx24118 attached.\n"); + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(cx24120_attach); + +static int cx24120_test_rom(struct cx24120_state *state) +{ + int err, ret; + + err = cx24120_readreg(state, 0xfd); + if (err & 4) { + ret = cx24120_readreg(state, 0xdf) & 0xfe; + err = cx24120_writereg(state, 0xdf, ret); + } + return err; +} + +static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL) + *snr = 0; + else + *snr = div_s64(c->cnr.stat[0].svalue, 100); + + return 0; +} + +static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) { + *ber = 0; + return 0; + } + + *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev; + state->ber_prev = c->post_bit_error.stat[0].uvalue; + + return 0; +} + +static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, + u8 flag); + +/* Check if we're running a command that needs to disable mpeg out */ +static void cx24120_check_cmd(struct cx24120_state *state, u8 id) +{ + switch (id) { + case CMD_TUNEREQUEST: + case CMD_CLOCK_READ: + case CMD_DISEQC_MSG1: + case CMD_DISEQC_MSG2: + case CMD_SETVOLTAGE: + case CMD_SETTONE: + case CMD_DISEQC_BURST: + cx24120_msg_mpeg_output_global_config(state, 0); + /* Old driver would do a msleep(100) here */ + default: + return; + } +} + +/* Send a message to the firmware */ +static int cx24120_message_send(struct cx24120_state *state, + struct cx24120_cmd *cmd) +{ + int ficus; + + if (state->mpeg_enabled) { + /* Disable mpeg out on certain commands */ + cx24120_check_cmd(state, cmd->id); + } + + cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id); + cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0], + cmd->len, 1); + cx24120_writereg(state, CX24120_REG_CMD_END, 0x01); + + ficus = 1000; + while (cx24120_readreg(state, CX24120_REG_CMD_END)) { + msleep(20); + ficus -= 20; + if (ficus == 0) { + err("Error sending message to firmware\n"); + return -EREMOTEIO; + } + } + dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id); + + return 0; +} + +/* Send a message and fill arg[] with the results */ +static int cx24120_message_sendrcv(struct cx24120_state *state, + struct cx24120_cmd *cmd, u8 numreg) +{ + int ret, i; + + if (numreg > CX24120_MAX_CMD_LEN) { + err("Too many registers to read. cmd->reg = %d", numreg); + return -EREMOTEIO; + } + + ret = cx24120_message_send(state, cmd); + if (ret != 0) + return ret; + + if (!numreg) + return 0; + + /* Read numreg registers starting from register cmd->len */ + for (i = 0; i < numreg; i++) + cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1)); + + return 0; +} + +static int cx24120_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->strength.stat[0].scale != FE_SCALE_RELATIVE) + *signal_strength = 0; + else + *signal_strength = c->strength.stat[0].uvalue; + + return 0; +} + +static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, + u8 enable) +{ + struct cx24120_cmd cmd; + int ret; + + cmd.id = CMD_MPEG_ONOFF; + cmd.len = 4; + cmd.arg[0] = 0x01; + cmd.arg[1] = 0x00; + cmd.arg[2] = enable ? 0 : (u8)(-1); + cmd.arg[3] = 0x01; + + ret = cx24120_message_send(state, &cmd); + if (ret != 0) { + dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n", + enable ? "enable" : "disable"); + return ret; + } + + state->mpeg_enabled = enable; + dev_dbg(&state->i2c->dev, "MPEG output %s\n", + enable ? "enabled" : "disabled"); + + return 0; +} + +static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq) +{ + struct cx24120_cmd cmd; + struct cx24120_initial_mpeg_config i = + state->config->initial_mpeg_config; + + cmd.id = CMD_MPEG_INIT; + cmd.len = 7; + cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */ + cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01); + cmd.arg[2] = 0x05; + cmd.arg[3] = 0x02; + cmd.arg[4] = ((i.x2 >> 1) & 0x01); + cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f); + cmd.arg[6] = 0x10; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "\n"); + + /* + * Yes, cmd.len is set to zero. The old driver + * didn't specify any len, but also had a + * memset 0 before every use of the cmd struct + * which would have set it to zero. + * This quite probably needs looking into. + */ + cmd.id = CMD_DISEQC_BURST; + cmd.len = 0; + cmd.arg[0] = 0x00; + cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "(%d)\n", tone); + + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + err("Invalid tone=%d\n", tone); + return -EINVAL; + } + + cmd.id = CMD_SETTONE; + cmd.len = 4; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + + dev_dbg(&state->i2c->dev, "(%d)\n", voltage); + + cmd.id = CMD_SETVOLTAGE; + cmd.len = 2; + cmd.arg[0] = 0x00; + cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int back_count; + + dev_dbg(&state->i2c->dev, "\n"); + + cmd.id = CMD_DISEQC_MSG1; + cmd.len = 11; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x03; + cmd.arg[3] = 0x16; + cmd.arg[4] = 0x28; + cmd.arg[5] = 0x01; + cmd.arg[6] = 0x01; + cmd.arg[7] = 0x14; + cmd.arg[8] = 0x19; + cmd.arg[9] = 0x14; + cmd.arg[10] = 0x1e; + + if (cx24120_message_send(state, &cmd)) { + err("send 1st message(0x%x) failed\n", cmd.id); + return -EREMOTEIO; + } + + cmd.id = CMD_DISEQC_MSG2; + cmd.len = d->msg_len + 6; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x01; + cmd.arg[2] = 0x02; + cmd.arg[3] = 0x00; + cmd.arg[4] = 0x00; + cmd.arg[5] = d->msg_len; + + memcpy(&cmd.arg[6], &d->msg, d->msg_len); + + if (cx24120_message_send(state, &cmd)) { + err("send 2nd message(0x%x) failed\n", cmd.id); + return -EREMOTEIO; + } + + back_count = 500; + do { + if (!(cx24120_readreg(state, 0x93) & 0x01)) { + dev_dbg(&state->i2c->dev, "diseqc sequence sent\n"); + return 0; + } + msleep(20); + back_count -= 20; + } while (back_count); + + err("Too long waiting for diseqc.\n"); + return -ETIMEDOUT; +} + +static void cx24120_get_stats(struct cx24120_state *state) +{ + struct dvb_frontend *fe = &state->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_cmd cmd; + int ret, cnr, msecs; + u16 sig, ucb; + u32 ber; + + dev_dbg(&state->i2c->dev, "\n"); + + /* signal strength */ + if (state->fe_status & FE_HAS_SIGNAL) { + cmd.id = CMD_READ_SNR; + cmd.len = 1; + cmd.arg[0] = 0x00; + + ret = cx24120_message_send(state, &cmd); + if (ret != 0) { + err("error reading signal strength\n"); + return; + } + + /* raw */ + sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6; + sig = sig << 8; + sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L); + dev_dbg(&state->i2c->dev, + "signal strength from firmware = 0x%x\n", sig); + + /* cooked */ + sig = -100 * sig + 94324; + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = sig; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (state->fe_status & FE_HAS_VITERBI) { + cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8; + cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L); + dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr); + + /* guessed - seems about right */ + cnr = cnr * 100; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER & UCB require lock */ + if (!(state->fe_status & FE_HAS_LOCK)) { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + /* BER */ + if (time_after(jiffies, state->ber_jiffies_stats)) { + msecs = (state->berw_usecs + 500) / 1000; + state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs); + + ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24; + ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16; + ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8; + ber |= cx24120_readreg(state, CX24120_REG_BER_LL); + dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += ber; + + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE; + } + + /* UCB */ + if (time_after(jiffies, state->per_jiffies_stats)) { + state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000); + + ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8; + ucb |= cx24120_readreg(state, CX24120_REG_UCB_L); + dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb); + + /* handle reset */ + if (ucb < state->ucb_offset) + state->ucb_offset = c->block_error.stat[0].uvalue; + + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = ucb + state->ucb_offset; + + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].uvalue += state->bitrate / 8 / 208; + } +} + +static void cx24120_set_clock_ratios(struct dvb_frontend *fe); + +/* Read current tuning status */ +static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct cx24120_state *state = fe->demodulator_priv; + int lock; + + lock = cx24120_readreg(state, CX24120_REG_STATUS); + + dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock); + + *status = 0; + + if (lock & CX24120_HAS_SIGNAL) + *status = FE_HAS_SIGNAL; + if (lock & CX24120_HAS_CARRIER) + *status |= FE_HAS_CARRIER; + if (lock & CX24120_HAS_VITERBI) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC; + if (lock & CX24120_HAS_LOCK) + *status |= FE_HAS_LOCK; + + /* + * TODO: is FE_HAS_SYNC in the right place? + * Other cx241xx drivers have this slightly + * different + */ + + state->fe_status = *status; + cx24120_get_stats(state); + + /* Set the clock once tuned in */ + if (state->need_clock_set && *status & FE_HAS_LOCK) { + /* Set clock ratios */ + cx24120_set_clock_ratios(fe); + + /* Old driver would do a msleep(200) here */ + + /* Renable mpeg output */ + if (!state->mpeg_enabled) + cx24120_msg_mpeg_output_global_config(state, 1); + + state->need_clock_set = 0; + } + + return 0; +} + +/* + * FEC & modulation lookup table + * Used for decoding the REG_FECMODE register + * once tuned in. + */ +struct cx24120_modfec { + enum fe_delivery_system delsys; + enum fe_modulation mod; + enum fe_code_rate fec; + u8 val; +}; + +static const struct cx24120_modfec modfec_lookup_table[] = { + /*delsys mod fec val */ + { SYS_DVBS, QPSK, FEC_1_2, 0x01 }, + { SYS_DVBS, QPSK, FEC_2_3, 0x02 }, + { SYS_DVBS, QPSK, FEC_3_4, 0x03 }, + { SYS_DVBS, QPSK, FEC_4_5, 0x04 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x05 }, + { SYS_DVBS, QPSK, FEC_6_7, 0x06 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x07 }, + + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, +}; + +/* Retrieve current fec, modulation & pilot values */ +static int cx24120_get_fec(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + int idx; + int ret; + int fec; + + ret = cx24120_readreg(state, CX24120_REG_FECMODE); + fec = ret & 0x3f; /* Lower 6 bits */ + + dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec); + + for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) { + if (modfec_lookup_table[idx].delsys != state->dcur.delsys) + continue; + if (modfec_lookup_table[idx].val != fec) + continue; + + break; /* found */ + } + + if (idx >= ARRAY_SIZE(modfec_lookup_table)) { + dev_dbg(&state->i2c->dev, "couldn't find fec!\n"); + return -EINVAL; + } + + /* save values back to cache */ + c->modulation = modfec_lookup_table[idx].mod; + c->fec_inner = modfec_lookup_table[idx].fec; + c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF; + + dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n", + c->modulation, c->fec_inner, c->pilot); + + return 0; +} + +/* Calculate ber window time */ +static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate) +{ + struct dvb_frontend *fe = &state->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u64 tmp; + + /* + * Calculate bitrate from rate in the clock ratios table. + * This isn't *exactly* right but close enough. + */ + tmp = (u64)c->symbol_rate * rate; + do_div(tmp, 256); + state->bitrate = tmp; + + /* usecs per ber window */ + tmp = 1000000ULL * CX24120_BER_WSIZE; + do_div(tmp, state->bitrate); + state->berw_usecs = tmp; + + dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n", + state->bitrate, state->berw_usecs); +} + +/* + * Clock ratios lookup table + * + * Values obtained from much larger table in old driver + * which had numerous entries which would never match. + * + * There's probably some way of calculating these but I + * can't determine the pattern + */ +struct cx24120_clock_ratios_table { + enum fe_delivery_system delsys; + enum fe_pilot pilot; + enum fe_modulation mod; + enum fe_code_rate fec; + u32 m_rat; + u32 n_rat; + u32 rate; +}; + +static const struct cx24120_clock_ratios_table clock_ratios_table[] = { + /*delsys pilot mod fec m_rat n_rat rate */ + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_1_2, 273088, 254505, 274 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_5, 17272, 13395, 330 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_2_3, 24344, 16967, 367 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_4, 410788, 254505, 413 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_4_5, 438328, 254505, 440 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_5_6, 30464, 16967, 459 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_8_9, 487832, 254505, 490 }, + { SYS_DVBS2, PILOT_OFF, QPSK, FEC_9_10, 493952, 254505, 496 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5, 328168, 169905, 494 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3, 24344, 11327, 550 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4, 410788, 169905, 618 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6, 30464, 11327, 688 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9, 487832, 169905, 735 }, + { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952, 169905, 744 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_1_2, 273088, 260709, 268 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_5, 328168, 260709, 322 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_2_3, 121720, 86903, 358 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_4, 410788, 260709, 403 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_4_5, 438328, 260709, 430 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_5_6, 152320, 86903, 448 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_8_9, 487832, 260709, 479 }, + { SYS_DVBS2, PILOT_ON, QPSK, FEC_9_10, 493952, 260709, 485 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_5, 328168, 173853, 483 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_2_3, 121720, 57951, 537 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_4, 410788, 173853, 604 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_5_6, 152320, 57951, 672 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_8_9, 487832, 173853, 718 }, + { SYS_DVBS2, PILOT_ON, PSK_8, FEC_9_10, 493952, 173853, 727 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_1_2, 152592, 152592, 256 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_2_3, 305184, 228888, 341 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_3_4, 457776, 305184, 384 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_5_6, 762960, 457776, 427 }, + { SYS_DVBS, PILOT_OFF, QPSK, FEC_7_8, 1068144, 610368, 448 }, +}; + +/* Set clock ratio from lookup table */ +static void cx24120_set_clock_ratios(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int ret, idx; + + /* Find fec, modulation, pilot */ + ret = cx24120_get_fec(fe); + if (ret != 0) + return; + + /* Find the clock ratios in the lookup table */ + for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) { + if (clock_ratios_table[idx].delsys != state->dcur.delsys) + continue; + if (clock_ratios_table[idx].mod != c->modulation) + continue; + if (clock_ratios_table[idx].fec != c->fec_inner) + continue; + if (clock_ratios_table[idx].pilot != c->pilot) + continue; + + break; /* found */ + } + + if (idx >= ARRAY_SIZE(clock_ratios_table)) { + info("Clock ratio not found - data reception in danger\n"); + return; + } + + /* Read current values? */ + cmd.id = CMD_CLOCK_READ; + cmd.len = 1; + cmd.arg[0] = 0x00; + ret = cx24120_message_sendrcv(state, &cmd, 6); + if (ret != 0) + return; + /* in cmd[0]-[5] - result */ + + dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n", + cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16), + cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16), + idx, + clock_ratios_table[idx].m_rat, + clock_ratios_table[idx].n_rat, + clock_ratios_table[idx].rate); + + /* Set the clock */ + cmd.id = CMD_CLOCK_SET; + cmd.len = 10; + cmd.arg[0] = 0; + cmd.arg[1] = 0x10; + cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff; + cmd.arg[3] = (clock_ratios_table[idx].m_rat >> 8) & 0xff; + cmd.arg[4] = (clock_ratios_table[idx].m_rat >> 0) & 0xff; + cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff; + cmd.arg[6] = (clock_ratios_table[idx].n_rat >> 8) & 0xff; + cmd.arg[7] = (clock_ratios_table[idx].n_rat >> 0) & 0xff; + cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff; + cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff; + + cx24120_message_send(state, &cmd); + + /* Calculate ber window rates for stat work */ + cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate); +} + +/* Set inversion value */ +static int cx24120_set_inversion(struct cx24120_state *state, + enum fe_spectral_inversion inversion) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", inversion); + + switch (inversion) { + case INVERSION_OFF: + state->dnxt.inversion_val = 0x00; + break; + case INVERSION_ON: + state->dnxt.inversion_val = 0x04; + break; + case INVERSION_AUTO: + state->dnxt.inversion_val = 0x0c; + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +/* FEC lookup table for tuning */ +struct cx24120_modfec_table { + enum fe_delivery_system delsys; + enum fe_modulation mod; + enum fe_code_rate fec; + u8 val; +}; + +static const struct cx24120_modfec_table modfec_table[] = { + /*delsys mod fec val */ + { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, + { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, + { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, + { SYS_DVBS, QPSK, FEC_6_7, 0x32 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x33 }, + + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, +}; + +/* Set fec_val & fec_mask values from delsys, modulation & fec */ +static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod, + enum fe_code_rate fec) +{ + int idx; + + dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec); + + state->dnxt.fec = fec; + + /* Lookup fec_val from modfec table */ + for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) { + if (modfec_table[idx].delsys != state->dnxt.delsys) + continue; + if (modfec_table[idx].mod != mod) + continue; + if (modfec_table[idx].fec != fec) + continue; + + /* found */ + state->dnxt.fec_mask = 0x00; + state->dnxt.fec_val = modfec_table[idx].val; + return 0; + } + + if (state->dnxt.delsys == SYS_DVBS2) { + /* DVBS2 auto is 0x00/0x00 */ + state->dnxt.fec_mask = 0x00; + state->dnxt.fec_val = 0x00; + } else { + /* Set DVB-S to auto */ + state->dnxt.fec_val = 0x2e; + state->dnxt.fec_mask = 0xac; + } + + return 0; +} + +/* Set pilot */ +static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", pilot); + + /* Pilot only valid in DVBS2 */ + if (state->dnxt.delsys != SYS_DVBS2) { + state->dnxt.pilot_val = CX24120_PILOT_OFF; + return 0; + } + + switch (pilot) { + case PILOT_OFF: + state->dnxt.pilot_val = CX24120_PILOT_OFF; + break; + case PILOT_ON: + state->dnxt.pilot_val = CX24120_PILOT_ON; + break; + case PILOT_AUTO: + default: + state->dnxt.pilot_val = CX24120_PILOT_AUTO; + } + + return 0; +} + +/* Set symbol rate */ +static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate) +{ + dev_dbg(&state->i2c->dev, "(%d)\n", rate); + + state->dnxt.symbol_rate = rate; + + /* Check symbol rate */ + if (rate > 31000000) { + state->dnxt.clkdiv = (-(rate < 31000001) & 3) + 2; + state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4; + } else { + state->dnxt.clkdiv = 3; + state->dnxt.ratediv = 6; + } + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void cx24120_clone_params(struct dvb_frontend *fe) +{ + struct cx24120_state *state = fe->demodulator_priv; + + state->dcur = state->dnxt; +} + +static int cx24120_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + int ret; + + switch (c->delivery_system) { + case SYS_DVBS2: + dev_dbg(&state->i2c->dev, "DVB-S2\n"); + break; + case SYS_DVBS: + dev_dbg(&state->i2c->dev, "DVB-S\n"); + break; + default: + dev_dbg(&state->i2c->dev, + "delivery system(%d) not supported\n", + c->delivery_system); + ret = -EINVAL; + break; + } + + state->dnxt.delsys = c->delivery_system; + state->dnxt.modulation = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.pilot = c->pilot; + + ret = cx24120_set_inversion(state, c->inversion); + if (ret != 0) + return ret; + + ret = cx24120_set_fec(state, c->modulation, c->fec_inner); + if (ret != 0) + return ret; + + ret = cx24120_set_pilot(state, c->pilot); + if (ret != 0) + return ret; + + ret = cx24120_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24120_clone_params(fe); + + dev_dbg(&state->i2c->dev, + "delsys = %d\n", state->dcur.delsys); + dev_dbg(&state->i2c->dev, + "modulation = %d\n", state->dcur.modulation); + dev_dbg(&state->i2c->dev, + "frequency = %d\n", state->dcur.frequency); + dev_dbg(&state->i2c->dev, + "pilot = %d (val = 0x%02x)\n", + state->dcur.pilot, state->dcur.pilot_val); + dev_dbg(&state->i2c->dev, + "symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n", + state->dcur.symbol_rate, + state->dcur.clkdiv, state->dcur.ratediv); + dev_dbg(&state->i2c->dev, + "FEC = %d (mask/val = 0x%02x/0x%02x)\n", + state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); + dev_dbg(&state->i2c->dev, + "Inversion = %d (val = 0x%02x)\n", + state->dcur.inversion, state->dcur.inversion_val); + + /* Flag that clock needs to be set after tune */ + state->need_clock_set = 1; + + /* Tune in */ + cmd.id = CMD_TUNEREQUEST; + cmd.len = 15; + cmd.arg[0] = 0; + cmd.arg[1] = (state->dcur.frequency & 0xff0000) >> 16; + cmd.arg[2] = (state->dcur.frequency & 0x00ff00) >> 8; + cmd.arg[3] = (state->dcur.frequency & 0x0000ff); + cmd.arg[4] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; + cmd.arg[5] = ((state->dcur.symbol_rate / 1000) & 0x00ff); + cmd.arg[6] = state->dcur.inversion; + cmd.arg[7] = state->dcur.fec_val | state->dcur.pilot_val; + cmd.arg[8] = CX24120_SEARCH_RANGE_KHZ >> 8; + cmd.arg[9] = CX24120_SEARCH_RANGE_KHZ & 0xff; + cmd.arg[10] = 0; /* maybe rolloff? */ + cmd.arg[11] = state->dcur.fec_mask; + cmd.arg[12] = state->dcur.ratediv; + cmd.arg[13] = state->dcur.clkdiv; + cmd.arg[14] = 0; + + /* Send tune command */ + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return ret; + + /* Write symbol rate values */ + ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv); + ret = cx24120_readreg(state, CX24120_REG_RATEDIV); + ret &= 0xfffffff0; + ret |= state->dcur.ratediv; + ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret); + + return 0; +} + +/* Set vco from config */ +static int cx24120_set_vco(struct cx24120_state *state) +{ + struct cx24120_cmd cmd; + u32 nxtal_khz, vco; + u64 inv_vco; + u32 xtal_khz = state->config->xtal_khz; + + nxtal_khz = xtal_khz * 4; + vco = nxtal_khz * 10; + inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco); + + dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n", + xtal_khz, vco, inv_vco); + + cmd.id = CMD_VCO_SET; + cmd.len = 12; + cmd.arg[0] = (vco >> 16) & 0xff; + cmd.arg[1] = (vco >> 8) & 0xff; + cmd.arg[2] = vco & 0xff; + cmd.arg[3] = (inv_vco >> 8) & 0xff; + cmd.arg[4] = (inv_vco) & 0xff; + cmd.arg[5] = 0x03; + cmd.arg[6] = (nxtal_khz >> 8) & 0xff; + cmd.arg[7] = nxtal_khz & 0xff; + cmd.arg[8] = 0x06; + cmd.arg[9] = 0x03; + cmd.arg[10] = (xtal_khz >> 16) & 0xff; + cmd.arg[11] = xtal_khz & 0xff; + + return cx24120_message_send(state, &cmd); +} + +static int cx24120_init(struct dvb_frontend *fe) +{ + const struct firmware *fw; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + struct cx24120_cmd cmd; + u8 reg; + int ret, i; + unsigned char vers[4]; + + if (state->cold_init) + return 0; + + /* ???? */ + cx24120_writereg(state, 0xea, 0x00); + cx24120_test_rom(state); + reg = cx24120_readreg(state, 0xfb) & 0xfe; + cx24120_writereg(state, 0xfb, reg); + reg = cx24120_readreg(state, 0xfc) & 0xfe; + cx24120_writereg(state, 0xfc, reg); + cx24120_writereg(state, 0xc3, 0x04); + cx24120_writereg(state, 0xc4, 0x04); + cx24120_writereg(state, 0xce, 0x00); + cx24120_writereg(state, 0xcf, 0x00); + reg = cx24120_readreg(state, 0xea) & 0xfe; + cx24120_writereg(state, 0xea, reg); + cx24120_writereg(state, 0xeb, 0x0c); + cx24120_writereg(state, 0xec, 0x06); + cx24120_writereg(state, 0xed, 0x05); + cx24120_writereg(state, 0xee, 0x03); + cx24120_writereg(state, 0xef, 0x05); + cx24120_writereg(state, 0xf3, 0x03); + cx24120_writereg(state, 0xf4, 0x44); + + for (i = 0; i < 3; i++) { + cx24120_writereg(state, 0xf0 + i, 0x04); + cx24120_writereg(state, 0xe6 + i, 0x02); + } + + cx24120_writereg(state, 0xea, (reg | 0x01)); + for (i = 0; i < 6; i += 2) { + cx24120_writereg(state, 0xc5 + i, 0x00); + cx24120_writereg(state, 0xc6 + i, 0x00); + } + + cx24120_writereg(state, 0xe4, 0x03); + cx24120_writereg(state, 0xeb, 0x0a); + + dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n", + CX24120_FIRMWARE); + + ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE); + if (ret) { + err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE, + ret); + return ret; + } + + dev_dbg(&state->i2c->dev, + "Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n", + (int)fw->size, /* firmware_size in bytes */ + fw->data[0], /* fw 1st byte */ + fw->data[1], /* fw 2d byte */ + fw->data[fw->size - 2], /* fw before last byte */ + fw->data[fw->size - 1]); /* fw last byte */ + + cx24120_test_rom(state); + reg = cx24120_readreg(state, 0xfb) & 0xfe; + cx24120_writereg(state, 0xfb, reg); + cx24120_writereg(state, 0xe0, 0x76); + cx24120_writereg(state, 0xf7, 0x81); + cx24120_writereg(state, 0xf8, 0x00); + cx24120_writereg(state, 0xf9, 0x00); + cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00); + cx24120_writereg(state, 0xf7, 0xc0); + cx24120_writereg(state, 0xe0, 0x00); + reg = (fw->size - 2) & 0x00ff; + cx24120_writereg(state, 0xf8, reg); + reg = ((fw->size - 2) >> 8) & 0x00ff; + cx24120_writereg(state, 0xf9, reg); + cx24120_writereg(state, 0xf7, 0x00); + cx24120_writereg(state, 0xdc, 0x00); + cx24120_writereg(state, 0xdc, 0x07); + msleep(500); + + /* Check final byte matches final byte of firmware */ + reg = cx24120_readreg(state, 0xe1); + if (reg == fw->data[fw->size - 1]) { + dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n"); + ret = 0; + } else { + err("Firmware upload failed. Last byte returned=0x%x\n", ret); + ret = -EREMOTEIO; + } + cx24120_writereg(state, 0xdc, 0x00); + release_firmware(fw); + if (ret != 0) + return ret; + + /* Start tuner */ + cmd.id = CMD_START_TUNER; + cmd.len = 3; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + + if (cx24120_message_send(state, &cmd) != 0) { + err("Error tuner start! :(\n"); + return -EREMOTEIO; + } + + /* Set VCO */ + ret = cx24120_set_vco(state); + if (ret != 0) { + err("Error set VCO! :(\n"); + return ret; + } + + /* set bandwidth */ + cmd.id = CMD_BANDWIDTH; + cmd.len = 12; + cmd.arg[0] = 0x00; + cmd.arg[1] = 0x00; + cmd.arg[2] = 0x00; + cmd.arg[3] = 0x00; + cmd.arg[4] = 0x05; + cmd.arg[5] = 0x02; + cmd.arg[6] = 0x02; + cmd.arg[7] = 0x00; + cmd.arg[8] = 0x05; + cmd.arg[9] = 0x02; + cmd.arg[10] = 0x02; + cmd.arg[11] = 0x00; + + if (cx24120_message_send(state, &cmd)) { + err("Error set bandwidth!\n"); + return -EREMOTEIO; + } + + reg = cx24120_readreg(state, 0xba); + if (reg > 3) { + dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret); + err("Error initialising tuner!\n"); + return -EREMOTEIO; + } + + dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n"); + + /* Initialise mpeg outputs */ + cx24120_writereg(state, 0xeb, 0x0a); + if (cx24120_msg_mpeg_output_global_config(state, 0) || + cx24120_msg_mpeg_output_config(state, 0) || + cx24120_msg_mpeg_output_config(state, 1) || + cx24120_msg_mpeg_output_config(state, 2)) { + err("Error initialising mpeg output. :(\n"); + return -EREMOTEIO; + } + + /* Set size of BER window */ + cmd.id = CMD_BER_CTRL; + cmd.len = 3; + cmd.arg[0] = 0x00; + cmd.arg[1] = CX24120_BER_WINDOW; + cmd.arg[2] = CX24120_BER_WINDOW; + if (cx24120_message_send(state, &cmd)) { + err("Error setting ber window\n"); + return -EREMOTEIO; + } + + /* Firmware CMD 35: Get firmware version */ + cmd.id = CMD_FWVERSION; + cmd.len = 1; + for (i = 0; i < 4; i++) { + cmd.arg[0] = i; + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return ret; + vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX); + } + info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]); + + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + state->cold_init = 1; + + return 0; +} + +static int cx24120_tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, unsigned int *delay, + enum fe_status *status) +{ + struct cx24120_state *state = fe->demodulator_priv; + int ret; + + dev_dbg(&state->i2c->dev, "(%d)\n", re_tune); + + /* TODO: Do we need to set delay? */ + + if (re_tune) { + ret = cx24120_set_frontend(fe); + if (ret) + return ret; + } + + return cx24120_read_status(fe, status); +} + +static int cx24120_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int cx24120_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int cx24120_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct cx24120_state *state = fe->demodulator_priv; + u8 freq1, freq2, freq3; + + dev_dbg(&state->i2c->dev, "\n"); + + /* don't return empty data if we're not tuned in */ + if ((state->fe_status & FE_HAS_LOCK) == 0) + return 0; + + /* Get frequency */ + freq1 = cx24120_readreg(state, CX24120_REG_FREQ1); + freq2 = cx24120_readreg(state, CX24120_REG_FREQ2); + freq3 = cx24120_readreg(state, CX24120_REG_FREQ3); + c->frequency = (freq3 << 16) | (freq2 << 8) | freq1; + dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency); + + /* Get modulation, fec, pilot */ + cx24120_get_fec(fe); + + return 0; +} + +static void cx24120_release(struct dvb_frontend *fe) +{ + struct cx24120_state *state = fe->demodulator_priv; + + dev_dbg(&state->i2c->dev, "Clear state structure\n"); + kfree(state); +} + +static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct cx24120_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) { + *ucblocks = 0; + return 0; + } + + *ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset; + + return 0; +} + +static struct dvb_frontend_ops cx24120_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Conexant CX24120/CX24118", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + .release = cx24120_release, + + .init = cx24120_init, + .sleep = cx24120_sleep, + + .tune = cx24120_tune, + .get_frontend_algo = cx24120_get_algo, + .set_frontend = cx24120_set_frontend, + + .get_frontend = cx24120_get_frontend, + .read_status = cx24120_read_status, + .read_ber = cx24120_read_ber, + .read_signal_strength = cx24120_read_signal_strength, + .read_snr = cx24120_read_snr, + .read_ucblocks = cx24120_read_ucblocks, + + .diseqc_send_master_cmd = cx24120_send_diseqc_msg, + + .diseqc_send_burst = cx24120_diseqc_send_burst, + .set_tone = cx24120_set_tone, + .set_voltage = cx24120_set_voltage, +}; + +MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware"); +MODULE_AUTHOR("Jemma Denson"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/cx24120.h b/kernel/drivers/media/dvb-frontends/cx24120.h new file mode 100644 index 000000000..f0970423e --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/cx24120.h @@ -0,0 +1,58 @@ +/* + * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver + * + * Copyright (C) 2008 Patrick Boettcher + * Copyright (C) 2009 Sergey Tyurin + * Updated 2012 by Jannis Achstetter + * Copyright (C) 2015 Jemma Denson + * + * 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 CX24120_H +#define CX24120_H + +#include +#include +#include + +struct cx24120_initial_mpeg_config { + u8 x1; + u8 x2; + u8 x3; +}; + +struct cx24120_config { + u8 i2c_addr; + u32 xtal_khz; + struct cx24120_initial_mpeg_config initial_mpeg_config; + + int (*request_firmware)(struct dvb_frontend *fe, + const struct firmware **fw, char *name); + + /* max bytes I2C provider can write at once */ + u16 i2c_wr_max; +}; + +#if IS_REACHABLE(CONFIG_DVB_CX24120) +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, + struct i2c_adapter *i2c) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* CX24120_H */ diff --git a/kernel/drivers/media/dvb-frontends/cx24123.c b/kernel/drivers/media/dvb-frontends/cx24123.c index 7975c6608..0fe7fb111 100644 --- a/kernel/drivers/media/dvb-frontends/cx24123.c +++ b/kernel/drivers/media/dvb-frontends/cx24123.c @@ -290,7 +290,7 @@ static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg) cx24123_i2c_writereg(state, state->config->demod_address, reg, val) static int cx24123_set_inversion(struct cx24123_state *state, - fe_spectral_inversion_t inversion) + enum fe_spectral_inversion inversion) { u8 nom_reg = cx24123_readreg(state, 0x0e); u8 auto_reg = cx24123_readreg(state, 0x10); @@ -318,7 +318,7 @@ static int cx24123_set_inversion(struct cx24123_state *state, } static int cx24123_get_inversion(struct cx24123_state *state, - fe_spectral_inversion_t *inversion) + enum fe_spectral_inversion *inversion) { u8 val; @@ -335,7 +335,7 @@ static int cx24123_get_inversion(struct cx24123_state *state, return 0; } -static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec) +static int cx24123_set_fec(struct cx24123_state *state, enum fe_code_rate fec) { u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; @@ -397,7 +397,7 @@ static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec) return 0; } -static int cx24123_get_fec(struct cx24123_state *state, fe_code_rate_t *fec) +static int cx24123_get_fec(struct cx24123_state *state, enum fe_code_rate *fec) { int ret; @@ -720,7 +720,7 @@ static int cx24123_initfe(struct dvb_frontend *fe) } static int cx24123_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx24123_state *state = fe->demodulator_priv; u8 val; @@ -795,7 +795,7 @@ static int cx24123_send_diseqc_msg(struct dvb_frontend *fe, } static int cx24123_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct cx24123_state *state = fe->demodulator_priv; int val, tone; @@ -831,7 +831,7 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend *fe, return 0; } -static int cx24123_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int cx24123_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cx24123_state *state = fe->demodulator_priv; int sync = cx24123_readreg(state, 0x14); @@ -966,7 +966,7 @@ static int cx24123_get_frontend(struct dvb_frontend *fe) return 0; } -static int cx24123_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int cx24123_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct cx24123_state *state = fe->demodulator_priv; u8 val; @@ -995,7 +995,7 @@ static int cx24123_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { int retval = 0; @@ -1011,7 +1011,7 @@ static int cx24123_tune(struct dvb_frontend *fe, static int cx24123_get_algo(struct dvb_frontend *fe) { - return 1; /* FE_ALGO_HW */ + return DVBFE_ALGO_HW; } static void cx24123_release(struct dvb_frontend *fe) diff --git a/kernel/drivers/media/dvb-frontends/cx24123.h b/kernel/drivers/media/dvb-frontends/cx24123.h index 758aee5a0..975f3c926 100644 --- a/kernel/drivers/media/dvb-frontends/cx24123.h +++ b/kernel/drivers/media/dvb-frontends/cx24123.h @@ -50,7 +50,7 @@ static inline struct dvb_frontend *cx24123_attach( printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static struct i2c_adapter * +static inline struct i2c_adapter * cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/kernel/drivers/media/dvb-frontends/cxd2820r_c.c b/kernel/drivers/media/dvb-frontends/cxd2820r_c.c index 72b0e2db3..42fad6aa3 100644 --- a/kernel/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/kernel/drivers/media/dvb-frontends/cxd2820r_c.c @@ -259,7 +259,7 @@ int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks) return 0; } -int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status) +int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/cxd2820r_core.c b/kernel/drivers/media/dvb-frontends/cxd2820r_core.c index 490e09004..def6d21d1 100644 --- a/kernel/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/kernel/drivers/media/dvb-frontends/cxd2820r_core.c @@ -287,7 +287,8 @@ static int cxd2820r_set_frontend(struct dvb_frontend *fe) err: return ret; } -static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status) + +static int cxd2820r_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; int ret; @@ -501,7 +502,7 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) struct cxd2820r_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; - fe_status_t status = 0; + enum fe_status status = 0; dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, fe->dtv_property_cache.delivery_system); diff --git a/kernel/drivers/media/dvb-frontends/cxd2820r_priv.h b/kernel/drivers/media/dvb-frontends/cxd2820r_priv.h index 4b428959b..a0d53f01a 100644 --- a/kernel/drivers/media/dvb-frontends/cxd2820r_priv.h +++ b/kernel/drivers/media/dvb-frontends/cxd2820r_priv.h @@ -48,7 +48,7 @@ struct cxd2820r_priv { struct gpio_chip gpio_chip; #endif - fe_delivery_system_t delivery_system; + enum fe_delivery_system delivery_system; bool last_tune_failed; /* for switch between T and T2 tune */ }; @@ -80,7 +80,7 @@ int cxd2820r_get_frontend_c(struct dvb_frontend *fe); int cxd2820r_set_frontend_c(struct dvb_frontend *fe); -int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status); +int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status); int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber); @@ -103,7 +103,7 @@ int cxd2820r_get_frontend_t(struct dvb_frontend *fe); int cxd2820r_set_frontend_t(struct dvb_frontend *fe); -int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status); +int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status); int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber); @@ -126,7 +126,7 @@ int cxd2820r_get_frontend_t2(struct dvb_frontend *fe); int cxd2820r_set_frontend_t2(struct dvb_frontend *fe); -int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status); +int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status); int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber); diff --git a/kernel/drivers/media/dvb-frontends/cxd2820r_t.c b/kernel/drivers/media/dvb-frontends/cxd2820r_t.c index 008cb2ac8..21abf1b4e 100644 --- a/kernel/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/kernel/drivers/media/dvb-frontends/cxd2820r_t.c @@ -349,7 +349,7 @@ int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks) return 0; } -int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status) +int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/cxd2820r_t2.c b/kernel/drivers/media/dvb-frontends/cxd2820r_t2.c index 35fe364c7..4e028b41c 100644 --- a/kernel/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/kernel/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -284,7 +284,7 @@ error: return ret; } -int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status) +int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/cxd2841er.c b/kernel/drivers/media/dvb-frontends/cxd2841er.c new file mode 100644 index 000000000..fdffb2f0d --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/cxd2841er.c @@ -0,0 +1,2727 @@ +/* + * cxd2841er.c + * + * Sony CXD2441ER digital demodulator driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_math.h" +#include "dvb_frontend.h" +#include "cxd2841er.h" +#include "cxd2841er_priv.h" + +#define MAX_WRITE_REGSIZE 16 + +enum cxd2841er_state { + STATE_SHUTDOWN = 0, + STATE_SLEEP_S, + STATE_ACTIVE_S, + STATE_SLEEP_TC, + STATE_ACTIVE_TC +}; + +struct cxd2841er_priv { + struct dvb_frontend frontend; + struct i2c_adapter *i2c; + u8 i2c_addr_slvx; + u8 i2c_addr_slvt; + const struct cxd2841er_config *config; + enum cxd2841er_state state; + u8 system; +}; + +static const struct cxd2841er_cnr_data s_cn_data[] = { + { 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 }, + { 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 }, + { 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 }, + { 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 }, + { 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 }, + { 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 }, + { 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 }, + { 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 }, + { 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 }, + { 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 }, + { 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 }, + { 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 }, + { 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 }, + { 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 }, + { 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 }, + { 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 }, + { 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 }, + { 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 }, + { 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 }, + { 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 }, + { 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 }, + { 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 }, + { 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 }, + { 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 }, + { 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 }, + { 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 }, + { 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 }, + { 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 }, + { 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 }, + { 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 }, + { 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 }, + { 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 }, + { 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 }, + { 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 }, + { 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 }, + { 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 }, + { 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 }, + { 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 }, + { 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 }, + { 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 }, + { 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 }, + { 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 }, + { 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 }, + { 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 }, + { 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 }, + { 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 }, + { 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 }, + { 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 }, + { 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 }, + { 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 }, + { 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 }, + { 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 }, + { 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 }, + { 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 }, + { 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 }, + { 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 }, + { 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 }, + { 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 }, + { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +static const struct cxd2841er_cnr_data s2_cn_data[] = { + { 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 }, + { 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 }, + { 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 }, + { 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 }, + { 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 }, + { 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 }, + { 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 }, + { 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 }, + { 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 }, + { 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 }, + { 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 }, + { 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 }, + { 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 }, + { 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 }, + { 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 }, + { 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 }, + { 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 }, + { 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 }, + { 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 }, + { 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 }, + { 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 }, + { 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 }, + { 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 }, + { 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 }, + { 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 }, + { 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 }, + { 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 }, + { 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 }, + { 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 }, + { 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 }, + { 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 }, + { 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 }, + { 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 }, + { 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 }, + { 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 }, + { 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 }, + { 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 }, + { 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 }, + { 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 }, + { 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 }, + { 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 }, + { 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 }, + { 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 }, + { 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 }, + { 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 }, + { 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 }, + { 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 }, + { 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 }, + { 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 }, + { 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 }, + { 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 }, + { 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 }, + { 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 }, + { 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 }, + { 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 }, + { 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 }, + { 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 }, + { 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 }, + { 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +#define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5)) + +static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 write, + 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); +} + +static int cxd2841er_write_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[1] = { + { + .addr = i2c_addr, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n", + KBUILD_MODNAME, ret, i2c_addr, reg, len); + return ret; + } + return 0; +} + +static int cxd2841er_write_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 val) +{ + return cxd2841er_write_regs(priv, addr, reg, &val, 1); +} + +static int cxd2841er_read_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val, u32 len) +{ + int ret; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[2] = { + { + .addr = i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + 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 = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, i2c_addr, reg); + return ret; + } + return 0; +} + +static int cxd2841er_read_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val) +{ + return cxd2841er_read_regs(priv, addr, reg, val, 1); +} + +static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = cxd2841er_read_reg(priv, addr, reg, &rdata); + if (res) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return cxd2841er_write_reg(priv, addr, reg, data); +} + +static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv, + u32 symbol_rate) +{ + u32 reg_value = 0; + u8 data[3] = {0, 0, 0}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* + * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5 + * = ((symbolRateKSps * 2^14) + 500) / 1000 + * = ((symbolRateKSps * 16384) + 500) / 1000 + */ + reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000); + if ((reg_value == 0) || (reg_value > 0xFFFFF)) { + dev_err(&priv->i2c->dev, + "%s(): reg_value is out of range\n", __func__); + return -EINVAL; + } + data[0] = (u8)((reg_value >> 16) & 0x0F); + data[1] = (u8)((reg_value >> 8) & 0xFF); + data[2] = (u8)(reg_value & 0xFF); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3); + return 0; +} + +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system); + +static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv, + u8 system, u32 symbol_rate) +{ + int ret; + u8 data[4] = { 0, 0, 0, 0 }; + + if (priv->state != STATE_SLEEP_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, (int)priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBS); + /* Set demod mode */ + if (system == SYS_DVBS) { + data[0] = 0x0A; + } else if (system == SYS_DVBS2) { + data[0] = 0x0B; + } else { + dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n", + __func__, system); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]); + /* DVB-S/S2 */ + data[0] = 0x00; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable S/S2 auto detection 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* Enable S/S2 auto detection 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* Enable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Enable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0xA3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3); + cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00); + data[0] = 0x07; + data[1] = 0x3B; + data[2] = 0x08; + data[3] = 0xC5; + /* Set SLV-T Bank : 0xAB */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab); + cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4); + data[0] = 0x05; + data[1] = 0x80; + data[2] = 0x0A; + data[3] = 0x80; + cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4); + data[0] = 0x0C; + data[1] = 0xCC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2); + /* Set demod parameter */ + ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate); + if (ret != 0) + return ret; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10); + /* disable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_S; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_retune_active(struct cxd2841er_priv *priv, + struct dtv_frontend_properties *p) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S && + priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + if (priv->state == STATE_ACTIVE_S) + return cxd2841er_dvbs2_set_symbol_rate( + priv, p->symbol_rate / 1000); + else if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + return cxd2841er_sleep_tc_to_active_t_band( + priv, p->bandwidth_hz); + case SYS_DVBT2: + return cxd2841er_sleep_tc_to_active_t2_band( + priv, p->bandwidth_hz); + case SYS_DVBC_ANNEX_A: + return cxd2841er_sleep_tc_to_active_c_band( + priv, 8000000); + } + } + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; +} + +static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00); + /* disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + /* SADC Bias ON */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + /* disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* disable S/S2 auto detection1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable S/S2 auto detection2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* Disable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-T2 setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-C setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + /* Set X'tal clock to 20.5Mhz */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* enable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F); + /* enable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + /* Set X'tal clock to 20.5Mhz */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_tune_done(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); + /* SW Reset */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01); + /* Enable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00); + return 0; +} + +/* Set TS parallel mode */ +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system) +{ + u8 serial_ts, ts_rate_ctrl_off, ts_in_off; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts); + cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off); + cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off); + dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n", + __func__, serial_ts, ts_rate_ctrl_off, ts_in_off); + + /* + * slave Bank Addr Bit default Name + * 00h D9h [7:0] 8'h08 OTSCKPERIOD + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08); + /* + * Disable TS IF Clock + * slave Bank Addr Bit default Name + * 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01); + /* + * slave Bank Addr Bit default Name + * 00h 33h [1:0] 2'b01 OREG_CKSEL_TSIF + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, 0x00, 0x03); + /* + * Enable TS IF Clock + * slave Bank Addr Bit default Name + * 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01); + + if (system == SYS_DVBT) { + /* Enable parity period for DVB-T */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } else if (system == SYS_DVBC_ANNEX_A) { + /* Enable parity period for DVB-C */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } +} + +static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) +{ + u8 chip_id; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + return chip_id; +} + +static int cxd2841er_read_status_s(struct dvb_frontend *fe, + enum fe_status *status) +{ + u8 reg = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + *status = 0; + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 11h [2] ITSLOCK + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x11, ®); + if (reg & 0x04) { + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + } + dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status); + return 0; +} + +static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + if (priv->system == SYS_DVBT) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + } else { + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + } + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + if ((data & 0x07) == 0x07) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid hardware state detected\n", __func__); + *sync = 0; + *tslock = 0; + *unlock = 0; + } else { + *sync = ((data & 0x07) == 0x6 ? 1 : 0); + *tslock = ((data & 0x20) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) +{ + u8 data; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data); + if ((data & 0x01) == 0) { + *tslock = 0; + } else { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + *tslock = ((data & 0x20) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_tc(struct dvb_frontend *fe, + enum fe_status *status) +{ + int ret = 0; + u8 sync = 0; + u8 tslock = 0; + u8 unlock = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + *status = 0; + if (priv->state == STATE_ACTIVE_TC) { + if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) { + ret = cxd2841er_read_status_t_t2( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_DVBC_ANNEX_A) { + ret = cxd2841er_read_status_c(priv, &tslock); + if (ret) + goto done; + if (tslock) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + } +done: + dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status); + return ret; +} + +static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, + int *offset) +{ + u8 data[3]; + u8 is_hs_mode; + s32 cfrl_ctrlval; + s32 temp_div, temp_q, temp_r; + + if (priv->state != STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* + * Get High Sampling Rate mode + * slave Bank Addr Bit Signal name + * A0h 10h [0] ITRL_LOCK + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]); + if (data[0] & 0x01) { + /* + * slave Bank Addr Bit Signal name + * A0h 50h [4] IHSMODE + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]); + is_hs_mode = (data[0] & 0x10 ? 1 : 0); + } else { + dev_dbg(&priv->i2c->dev, + "%s(): unable to detect sampling rate mode\n", + __func__); + return -EINVAL; + } + /* + * slave Bank Addr Bit Signal name + * A0h 45h [4:0] ICFRL_CTRLVAL[20:16] + * A0h 46h [7:0] ICFRL_CTRLVAL[15:8] + * A0h 47h [7:0] ICFRL_CTRLVAL[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3); + cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) | + (((u32)data[1] & 0xFF) << 8) | + ((u32)data[2] & 0xFF), 20); + temp_div = (is_hs_mode ? 1048576 : 1572864); + if (cfrl_ctrlval > 0) { + temp_q = div_s64_rem(97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } else { + temp_q = div_s64_rem(-97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } + if (temp_r >= temp_div / 2) + temp_q++; + if (cfrl_ctrlval > 0) + temp_q *= -1; + *offset = temp_q; + return 0; +} + +static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT2) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 27); + switch (bandwidth) { + case 1712000: + *offset /= 582; + break; + case 5000000: + case 6000000: + case 7000000: + case 8000000: + *offset *= (bandwidth / 1000000); + *offset /= 940; + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + return 0; +} + +static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv, + int *offset) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBC_ANNEX_A) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data)); + *offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8) + | (u32)data[1], 13), 16384); + return 0; +} + +static int cxd2841er_read_packet_errors_t( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data)); + if (data[2] & 0x01) + *penum = ((u32)data[0] << 8) | (u32)data[1]; + return 0; +} + +static int cxd2841er_read_packet_errors_t2( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data)); + if (data[0] & 0x01) + *penum = ((u32)data[1] << 8) | (u32)data[2]; + return 0; +} + +static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) +{ + u8 data[11]; + u32 bit_error, bit_count; + u32 temp_q, temp_r; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 35h [0] IFVBER_VALID + * A0h 36h [5:0] IFVBER_BITERR[21:16] + * A0h 37h [7:0] IFVBER_BITERR[15:8] + * A0h 38h [7:0] IFVBER_BITERR[7:0] + * A0h 3Dh [5:0] IFVBER_BITNUM[21:16] + * A0h 3Eh [7:0] IFVBER_BITNUM[15:8] + * A0h 3Fh [7:0] IFVBER_BITNUM[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11); + if (data[0] & 0x01) { + bit_error = ((u32)(data[1] & 0x3F) << 16) | + ((u32)(data[2] & 0xFF) << 8) | + (u32)(data[3] & 0xFF); + bit_count = ((u32)(data[8] & 0x3F) << 16) | + ((u32)(data[9] & 0xFF) << 8) | + (u32)(data[10] & 0xFF); + /* + * BER = bitError / bitCount + * = (bitError * 10^7) / bitCount + * = ((bitError * 625 * 125 * 128) / bitCount + */ + if ((bit_count == 0) || (bit_error > bit_count)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_error %d, bit_count %d\n", + __func__, bit_error, bit_count); + return 0; + } + temp_q = div_u64_rem(10000000ULL * bit_error, + bit_count, &temp_r); + if (bit_count != 1 && temp_r >= bit_count / 2) + temp_q++; + return temp_q; + } + dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); + return 0; +} + + +static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv) +{ + u8 data[5]; + u32 bit_error, period; + u32 temp_q, temp_r; + u32 result = 0; + + /* Set SLV-T Bank : 0xB2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2); + /* + * slave Bank Addr Bit Signal name + * B2h 30h [0] IFLBER_VALID + * B2h 31h [3:0] IFLBER_BITERR[27:24] + * B2h 32h [7:0] IFLBER_BITERR[23:16] + * B2h 33h [7:0] IFLBER_BITERR[15:8] + * B2h 34h [7:0] IFLBER_BITERR[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5); + if (data[0] & 0x01) { + /* Bit error count */ + bit_error = ((u32)(data[1] & 0x0F) << 24) | + ((u32)(data[2] & 0xFF) << 16) | + ((u32)(data[3] & 0xFF) << 8) | + (u32)(data[4] & 0xFF); + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data); + /* Measurement period */ + period = (u32)(1 << (data[0] & 0x0F)); + if (period == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): period is 0\n", __func__); + return 0; + } + if (bit_error > (period * 64800)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_err 0x%x period 0x%x\n", + __func__, bit_error, period); + return 0; + } + /* + * BER = bitError / (period * 64800) + * = (bitError * 10^7) / (period * 64800) + * = (bitError * 10^5) / (period * 648) + * = (bitError * 12500) / (period * 81) + * = (bitError * 10) * 1250 / (period * 81) + */ + temp_q = div_u64_rem(12500ULL * bit_error, + period * 81, &temp_r); + if (temp_r >= period * 40) + temp_q++; + result = temp_q; + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + } + return result; +} + +static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) +{ + u8 data[4]; + u32 div, q, r; + u32 bit_err, period_exp, n_ldpc; + + *ber = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data)); + if (!(data[0] & 0x10)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return 0; + } + bit_err = ((u32)(data[0] & 0x0f) << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + (u32)data[3]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period_exp = data[0] & 0x0f; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22); + cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data); + n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800); + if (bit_err > ((1U << period_exp) * n_ldpc)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid BER value\n", __func__); + return -EINVAL; + } + if (period_exp >= 4) { + div = (1U << (period_exp - 4)) * (n_ldpc / 200); + q = div_u64_rem(3125ULL * bit_err, div, &r); + } else { + div = (1U << period_exp) * (n_ldpc / 200); + q = div_u64_rem(50000ULL * bit_err, div, &r); + } + *ber = (r >= div / 2) ? q + 1 : q; + return 0; +} + +static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber) +{ + u8 data[2]; + u32 div, q, r; + u32 bit_err, period; + + *ber = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data); + if (!(data[0] & 0x01)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return 0; + } + cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data)); + bit_err = ((u32)data[0] << 8) | (u32)data[1]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07)); + div = period / 128; + q = div_u64_rem(78125ULL * bit_err, div, &r); + *ber = (r >= div / 2) ? q + 1 : q; + return 0; +} + +static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys) +{ + u8 data[3]; + u32 res = 0, value; + int min_index, max_index, index; + static const struct cxd2841er_cnr_data *cn_data; + + /* Set SLV-T Bank : 0xA1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1); + /* + * slave Bank Addr Bit Signal name + * A1h 10h [0] ICPM_QUICKRDY + * A1h 11h [4:0] ICPM_QUICKCNDT[12:8] + * A1h 12h [7:0] ICPM_QUICKCNDT[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3); + if (data[0] & 0x01) { + value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF); + min_index = 0; + if (delsys == SYS_DVBS) { + cn_data = s_cn_data; + max_index = sizeof(s_cn_data) / + sizeof(s_cn_data[0]) - 1; + } else { + cn_data = s2_cn_data; + max_index = sizeof(s2_cn_data) / + sizeof(s2_cn_data[0]) - 1; + } + if (value >= cn_data[min_index].value) { + res = cn_data[min_index].cnr_x1000; + goto done; + } + if (value <= cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } + while ((max_index - min_index) > 1) { + index = (max_index + min_index) / 2; + if (value == cn_data[index].value) { + res = cn_data[index].cnr_x1000; + goto done; + } else if (value > cn_data[index].value) + max_index = index; + else + min_index = index; + if ((max_index - min_index) <= 1) { + if (value == cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } else { + res = cn_data[min_index].cnr_x1000; + goto done; + } + } + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + } +done: + return res; +} + +static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500; + return 0; +} + +static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 10876) + reg = 10876; + *snr = 10000 * ((intlog10(reg) - + intlog10(12600 - reg)) >> 24) + 32000; + return 0; +} + +static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv) +{ + u8 data[2]; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 1Fh [4:0] IRFAGC_GAIN[12:8] + * A0h 20h [7:0] IRFAGC_GAIN[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2); + return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3; +} + +static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + *ber = 0; + switch (p->delivery_system) { + case SYS_DVBS: + *ber = cxd2841er_mon_read_ber_s(priv); + break; + case SYS_DVBS2: + *ber = cxd2841er_mon_read_ber_s2(priv); + break; + case SYS_DVBT: + return cxd2841er_read_ber_t(priv, ber); + case SYS_DVBT2: + return cxd2841er_read_ber_t2(priv, ber); + default: + *ber = 0; + break; + } + return 0; +} + +static int cxd2841er_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + *strength = 65535 - cxd2841er_read_agc_gain_t_t2( + priv, p->delivery_system); + break; + case SYS_DVBS: + case SYS_DVBS2: + *strength = 65535 - cxd2841er_read_agc_gain_s(priv); + break; + default: + *strength = 0; + break; + } + return 0; +} + +static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 tmp = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + cxd2841er_read_snr_t(priv, &tmp); + break; + case SYS_DVBT2: + cxd2841er_read_snr_t2(priv, &tmp); + break; + case SYS_DVBS: + case SYS_DVBS2: + tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n", + __func__, p->delivery_system); + break; + } + *snr = tmp & 0xffff; + return 0; +} + +static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + cxd2841er_read_packet_errors_t(priv, ucblocks); + break; + case SYS_DVBT2: + cxd2841er_read_packet_errors_t2(priv, ucblocks); + break; + default: + *ucblocks = 0; + break; + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return 0; +} + +static int cxd2841er_dvbt2_set_profile( + struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile) +{ + u8 tune_mode; + u8 seq_not2d_time; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (profile) { + case DVBT2_PROFILE_BASE: + tune_mode = 0x01; + seq_not2d_time = 12; + break; + case DVBT2_PROFILE_LITE: + tune_mode = 0x05; + seq_not2d_time = 40; + break; + case DVBT2_PROFILE_ANY: + tune_mode = 0x00; + seq_not2d_time = 40; + break; + default: + return -EINVAL; + } + /* Set SLV-T Bank : 0x2E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e); + /* Set profile and tune mode */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07); + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + /* Set early unlock detection time */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time); + return 0; +} + +static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv, + u8 is_auto, u8 plp_id) +{ + if (is_auto) { + dev_dbg(&priv->i2c->dev, + "%s() using auto PLP selection\n", __func__); + } else { + dev_dbg(&priv->i2c->dev, + "%s() using manual PLP selection, ID %d\n", + __func__, plp_id); + } + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + if (!is_auto) { + /* Manual PLP selection mode. Set the data PLP Id. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id); + } + /* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01)); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u32 iffreq; + u8 b20_9f[5]; + u8 b10_a6[14]; + u8 b10_b6[3]; + u8 b10_d7; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (bandwidth) { + case 8000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x11; + b20_9f[1] = 0xf0; + b20_9f[2] = 0x00; + b20_9f[3] = 0x00; + b20_9f[4] = 0x00; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x26; + b10_a6[1] = 0xaf; + b10_a6[2] = 0x06; + b10_a6[3] = 0xcd; + b10_a6[4] = 0x13; + b10_a6[5] = 0xbb; + b10_a6[6] = 0x28; + b10_a6[7] = 0xba; + b10_a6[8] = 0x23; + b10_a6[9] = 0xa9; + b10_a6[10] = 0x1f; + b10_a6[11] = 0xa8; + b10_a6[12] = 0x2c; + b10_a6[13] = 0xc8; + iffreq = MAKE_IFFREQ_CONFIG(4.80); + b10_d7 = 0x00; + break; + case 7000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x14; + b20_9f[1] = 0x80; + b20_9f[2] = 0x00; + b20_9f[3] = 0x00; + b20_9f[4] = 0x00; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x2C; + b10_a6[1] = 0xBD; + b10_a6[2] = 0x02; + b10_a6[3] = 0xCF; + b10_a6[4] = 0x04; + b10_a6[5] = 0xF8; + b10_a6[6] = 0x23; + b10_a6[7] = 0xA6; + b10_a6[8] = 0x29; + b10_a6[9] = 0xB0; + b10_a6[10] = 0x26; + b10_a6[11] = 0xA9; + b10_a6[12] = 0x21; + b10_a6[13] = 0xA5; + iffreq = MAKE_IFFREQ_CONFIG(4.2); + b10_d7 = 0x02; + break; + case 6000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x17; + b20_9f[1] = 0xEA; + b20_9f[2] = 0xAA; + b20_9f[3] = 0xAA; + b20_9f[4] = 0xAA; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x27; + b10_a6[1] = 0xA7; + b10_a6[2] = 0x28; + b10_a6[3] = 0xB3; + b10_a6[4] = 0x02; + b10_a6[5] = 0xF0; + b10_a6[6] = 0x01; + b10_a6[7] = 0xE8; + b10_a6[8] = 0x00; + b10_a6[9] = 0xCF; + b10_a6[10] = 0x00; + b10_a6[11] = 0xE6; + b10_a6[12] = 0x23; + b10_a6[13] = 0xA4; + iffreq = MAKE_IFFREQ_CONFIG(3.6); + b10_d7 = 0x04; + break; + case 5000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x1C; + b20_9f[1] = 0xB3; + b20_9f[2] = 0x33; + b20_9f[3] = 0x33; + b20_9f[4] = 0x33; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x27; + b10_a6[1] = 0xA7; + b10_a6[2] = 0x28; + b10_a6[3] = 0xB3; + b10_a6[4] = 0x02; + b10_a6[5] = 0xF0; + b10_a6[6] = 0x01; + b10_a6[7] = 0xE8; + b10_a6[8] = 0x00; + b10_a6[9] = 0xCF; + b10_a6[10] = 0x00; + b10_a6[11] = 0xE6; + b10_a6[12] = 0x23; + b10_a6[13] = 0xA4; + iffreq = MAKE_IFFREQ_CONFIG(3.6); + b10_d7 = 0x06; + break; + case 1712000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x58; + b20_9f[1] = 0xE2; + b20_9f[2] = 0xAF; + b20_9f[3] = 0xE0; + b20_9f[4] = 0xBC; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x25; + b10_a6[1] = 0xA0; + b10_a6[2] = 0x36; + b10_a6[3] = 0x8D; + b10_a6[4] = 0x2E; + b10_a6[5] = 0x94; + b10_a6[6] = 0x28; + b10_a6[7] = 0x9B; + b10_a6[8] = 0x32; + b10_a6[9] = 0x90; + b10_a6[10] = 0x2C; + b10_a6[11] = 0x9D; + b10_a6[12] = 0x29; + b10_a6[13] = 0x99; + iffreq = MAKE_IFFREQ_CONFIG(3.5); + b10_d7 = 0x03; + break; + default: + return -EINVAL; + } + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20); + cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f)); + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x7a, + (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* Group delay equaliser sett. for ASCOT2E */ + cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6)); + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u8 b13_9c[2] = { 0x01, 0x14 }; + u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 }; + u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, + 0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 }; + u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 }; + u8 bw8mhz_b17_38[] = { 0x01, 0x02 }; + u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 }; + u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, + 0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 }; + u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 }; + u8 bw7mhz_b17_38[] = { 0x00, 0x03 }; + u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA }; + u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, + 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC }; + u8 bw6mhz_b17_38[] = { 0x00, 0x03 }; + u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 }; + u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, + 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 bw5mhz_b10_d9[] = { 0x26, 0x3C }; + u8 bw5mhz_b17_38[] = { 0x00, 0x03 }; + u8 b10_b6[3]; + u8 d7val; + u32 iffreq; + u8 *b10_9f; + u8 *b10_a6; + u8 *b10_d9; + u8 *b17_38; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + /* Echo performance optimization setting */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c)); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + switch (bandwidth) { + case 8000000: + b10_9f = bw8mhz_b10_9f; + b10_a6 = bw8mhz_b10_a6; + b10_d9 = bw8mhz_b10_d9; + b17_38 = bw8mhz_b17_38; + d7val = 0; + iffreq = MAKE_IFFREQ_CONFIG(4.80); + break; + case 7000000: + b10_9f = bw7mhz_b10_9f; + b10_a6 = bw7mhz_b10_a6; + b10_d9 = bw7mhz_b10_d9; + b17_38 = bw7mhz_b17_38; + d7val = 2; + iffreq = MAKE_IFFREQ_CONFIG(4.20); + break; + case 6000000: + b10_9f = bw6mhz_b10_9f; + b10_a6 = bw6mhz_b10_a6; + b10_d9 = bw6mhz_b10_d9; + b17_38 = bw6mhz_b17_38; + d7val = 4; + iffreq = MAKE_IFFREQ_CONFIG(3.60); + break; + case 5000000: + b10_9f = bw5mhz_b10_9f; + b10_a6 = bw5mhz_b10_a6; + b10_d9 = bw5mhz_b10_d9; + b17_38 = bw5mhz_b17_38; + d7val = 6; + iffreq = MAKE_IFFREQ_CONFIG(3.60); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs( + priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f)); + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6)); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7); + cxd2841er_write_regs( + priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9)); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs( + priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38)); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 bw7_8mhz_b10_a6[] = { + 0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8, + 0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB }; + u8 bw6mhz_b10_a6[] = { + 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 b10_b6[3]; + u32 iffreq; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + 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); + break; + case 6000000: + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, + bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6)); + iffreq = MAKE_IFFREQ_CONFIG(3.7); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + switch (bandwidth) { + case 8000000: + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x14, 0x1f); + break; + } + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + switch (bandwidth) { + case 8000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x0b, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x3e); + break; + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0xd6); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x08, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x6e); + break; + } + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + 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); + /* Set SLV-T Bank : 0x18 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + /* Pre-RS BER moniter setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + 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); + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + /* DVB-T2 initial setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8); + /* Set SLV-T Bank : 0x2a */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); + + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + 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); + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + /* Demod setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + cxd2841er_sleep_tc_to_active_c_band(priv, 8000000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_get_frontend(struct dvb_frontend *fe) +{ + enum fe_status status = 0; + u16 strength = 0, snr = 0; + u32 errors = 0, ber = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE_S) + cxd2841er_read_status_s(fe, &status); + else if (priv->state == STATE_ACTIVE_TC) + cxd2841er_read_status_tc(fe, &status); + + if (status & FE_HAS_LOCK) { + cxd2841er_read_signal_strength(fe, &strength); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->strength.stat[0].uvalue = strength; + cxd2841er_read_snr(fe, &snr); + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = snr; + cxd2841er_read_ucblocks(fe, &errors); + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = errors; + cxd2841er_read_ber(fe, &ber); + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue = ber; + } else { + 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; + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + return 0; +} + +static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) +{ + int ret = 0, i, timeout, carr_offset; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n", + __func__, + (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), + p->frequency, symbol_rate); + switch (priv->state) { + case STATE_SLEEP_S: + ret = cxd2841er_sleep_s_to_active_s( + priv, p->delivery_system, symbol_rate); + break; + case STATE_ACTIVE_S: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + goto done; + } + if (ret) { + 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); + cxd2841er_tune_done(priv); + timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150; + for (i = 0; i < timeout / CXD2841ER_DVBS_POLLING_INVL; i++) { + usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000, + (CXD2841ER_DVBS_POLLING_INVL + 2) * 1000); + cxd2841er_read_status_s(fe, &status); + if (status & FE_HAS_LOCK) + break; + } + if (status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carr_offset)) { + ret = -EINVAL; + goto done; + } + dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n", + __func__, carr_offset); + } +done: + return ret; +} + +static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) +{ + int ret = 0, timeout; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (p->delivery_system == SYS_DVBT) { + priv->system = SYS_DVBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBT2) { + priv->system = SYS_DVBT2; + cxd2841er_dvbt2_set_plp_config(priv, + (int)(p->stream_id > 255), p->stream_id); + cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE); + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t2(priv, + p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A || + p->delivery_system == SYS_DVBC_ANNEX_C) { + priv->system = SYS_DVBC_ANNEX_A; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_c( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, p->delivery_system); + ret = -EINVAL; + } + 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); + cxd2841er_tune_done(priv); + timeout = 2500; + while (timeout > 0) { + ret = cxd2841er_read_status_tc(fe, &status); + if (ret) + goto done; + if (status & FE_HAS_LOCK) + break; + msleep(20); + timeout -= 20; + } + if (timeout < 0) + dev_dbg(&priv->i2c->dev, + "%s(): LOCK wait timeout\n", __func__); +done: + return ret; +} + +static int cxd2841er_tune_s(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune); + if (re_tune) { + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + cxd2841er_read_status_s(fe, status); + if (*status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carrier_offset)) + return -EINVAL; + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_s(fe, status); +} + +static int cxd2841er_tune_tc(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune); + if (re_tune) { + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + cxd2841er_read_status_tc(fe, status); + if (*status & FE_HAS_LOCK) { + switch (priv->system) { + case SYS_DVBT: + case SYS_DVBT2: + ret = cxd2841er_get_carrier_offset_t2( + priv, p->bandwidth_hz, + &carrier_offset); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2841er_get_carrier_offset_c( + priv, &carrier_offset); + break; + default: + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + if (ret) + return ret; + dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n", + __func__, carrier_offset); + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_tc(fe, status); +} + +static int cxd2841er_sleep_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_active_s_to_sleep_s(fe->demodulator_priv); + cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv); + return 0; +} + +static int cxd2841er_sleep_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + cxd2841er_active_t_to_sleep_tc(priv); + break; + case SYS_DVBT2: + cxd2841er_active_t2_to_sleep_tc(priv); + break; + case SYS_DVBC_ANNEX_A: + cxd2841er_active_c_to_sleep_tc(priv); + break; + default: + dev_warn(&priv->i2c->dev, + "%s(): unknown delivery system %d\n", + __func__, priv->system); + } + } + if (priv->state != STATE_SLEEP_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_sleep_tc_to_shutdown(priv); + return 0; +} + +static int cxd2841er_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__, + (burst == SEC_MINI_A ? "A" : "B")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (burst == SEC_MINI_A ? 0 : 1); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data); + return 0; +} + +static int cxd2841er_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__, + (tone == SEC_TONE_ON ? "On" : "Off")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (tone == SEC_TONE_ON ? 1 : 0); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data); + return 0; +} + +static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + int i; + u8 data[12]; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, + "%s(): cmd->len %d\n", __func__, cmd->msg_len); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + /* DiDEqC enable */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01); + /* cmd1 length & data */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len); + memset(data, 0, sizeof(data)); + for (i = 0; i < cmd->msg_len && i < sizeof(data); i++) + data[i] = cmd->msg[i]; + cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data)); + /* repeat count for cmd1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1); + /* repeat count for cmd2: always 0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0); + /* start transmit */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01); + /* wait for 1 sec timeout */ + for (i = 0; i < 50; i++) { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data); + if (!data[0]) { + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd has been sent\n", __func__); + return 0; + } + msleep(20); + } + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd transmit timeout\n", __func__); + return -ETIMEDOUT; +} + +static void cxd2841er_release(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(priv); +} + +static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable); + cxd2841er_set_reg_bits( + priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01); + return 0; +} + +static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return DVBFE_ALGO_HW; +} + +static int cxd2841er_init_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_shutdown_to_sleep_s(priv); + /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01); + return 0; +} + +static int cxd2841er_init_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_shutdown_to_sleep_tc(priv); + /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, 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); + return 0; +} + +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; +static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; +static struct dvb_frontend_ops cxd2841er_dvbc_ops; + +static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c, + u8 system) +{ + u8 chip_id = 0; + const char *type; + struct cxd2841er_priv *priv = NULL; + + /* allocate memory for the internal state */ + priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c = i2c; + priv->config = cfg; + priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; + priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; + /* create dvb_frontend */ + switch (system) { + case SYS_DVBS: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + break; + case SYS_DVBT: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbt_t2_ops, + sizeof(struct dvb_frontend_ops)); + type = "T/T2"; + break; + case SYS_DVBC_ANNEX_A: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbc_ops, + sizeof(struct dvb_frontend_ops)); + type = "C/C2"; + break; + default: + kfree(priv); + return NULL; + } + priv->frontend.demodulator_priv = priv; + dev_info(&priv->i2c->dev, + "%s(): attaching CXD2841ER DVB-%s frontend\n", + __func__, type); + dev_info(&priv->i2c->dev, + "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", + __func__, priv->i2c, + priv->i2c_addr_slvx, priv->i2c_addr_slvt); + chip_id = cxd2841er_chip_id(priv); + if (chip_id != CXD2841ER_CHIP_ID) { + dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", + __func__, chip_id); + priv->frontend.demodulator_priv = NULL; + kfree(priv); + return NULL; + } + dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", + __func__, chip_id); + return &priv->frontend; +} + +struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBS); +} +EXPORT_SYMBOL(cxd2841er_attach_s); + +struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBT); +} +EXPORT_SYMBOL(cxd2841er_attach_t); + +struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A); +} +EXPORT_SYMBOL(cxd2841er_attach_c); + +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Sony CXD2841ER DVB-S/S2 demodulator", + .frequency_min = 500000, + .frequency_max = 2500000, + .frequency_stepsize = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK, + }, + .init = cxd2841er_init_s, + .sleep = cxd2841er_sleep_s, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_s, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_s, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo, + .set_tone = cxd2841er_set_tone, + .diseqc_send_burst = cxd2841er_send_burst, + .diseqc_send_master_cmd = cxd2841er_send_diseqc_msg, + .tune = cxd2841er_tune_s +}; + +static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2 }, + .info = { + .name = "Sony CXD2841ER DVB-T/T2 demodulator", + .caps = 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_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION, + .frequency_min = 42000000, + .frequency_max = 1002000000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_sleep_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo +}; + +static struct dvb_frontend_ops cxd2841er_dvbc_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "Sony CXD2841ER DVB-C demodulator", + .caps = 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_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_INVERSION_AUTO, + .frequency_min = 42000000, + .frequency_max = 1002000000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_sleep_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo, +}; + +MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov "); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/cxd2841er.h b/kernel/drivers/media/dvb-frontends/cxd2841er.h new file mode 100644 index 000000000..3472bdd58 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/cxd2841er.h @@ -0,0 +1,65 @@ +/* + * cxd2841er.h + * + * Sony CXD2441ER digital demodulator driver public definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 CXD2841ER_H +#define CXD2841ER_H + +#include +#include + +struct cxd2841er_config { + u8 i2c_addr; +}; + +#if IS_REACHABLE(CONFIG_DVB_CXD2841ER) +extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); + +extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); + +extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cxd2841er_attach_s( + struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_t( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_c( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/kernel/drivers/media/dvb-frontends/cxd2841er_priv.h b/kernel/drivers/media/dvb-frontends/cxd2841er_priv.h new file mode 100644 index 000000000..33e2f4952 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -0,0 +1,43 @@ +/* + * cxd2841er_priv.h + * + * Sony CXD2441ER digital demodulator driver internal definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 CXD2841ER_PRIV_H +#define CXD2841ER_PRIV_H + +#define I2C_SLVX 0 +#define I2C_SLVT 1 + +#define CXD2841ER_CHIP_ID 0xa7 + +#define CXD2841ER_DVBS_POLLING_INVL 10 + +struct cxd2841er_cnr_data { + u32 value; + int cnr_x1000; +}; + +enum cxd2841er_dvbt2_profile_t { + DVBT2_PROFILE_ANY = 0, + DVBT2_PROFILE_BASE = 1, + DVBT2_PROFILE_LITE = 2 +}; + +#endif diff --git a/kernel/drivers/media/dvb-frontends/dib0070.c b/kernel/drivers/media/dvb-frontends/dib0070.c index 3b024bfe9..0b8fb5dd1 100644 --- a/kernel/drivers/media/dvb-frontends/dib0070.c +++ b/kernel/drivers/media/dvb-frontends/dib0070.c @@ -58,10 +58,10 @@ struct dib0070_state { u16 wbd_ff_offset; u8 revision; - enum frontend_tune_state tune_state; - u32 current_rf; + enum frontend_tune_state tune_state; + u32 current_rf; - /* for the captrim binary search */ + /* for the captrim binary search */ s8 step; u16 adc_diff; @@ -72,7 +72,7 @@ struct dib0070_state { const struct dib0070_tuning *current_tune_table_index; const struct dib0070_lna_match *lna_match; - u8 wbd_gain_current; + u8 wbd_gain_current; u16 wbd_offset_3_3[2]; /* for the I2C transfer */ @@ -151,31 +151,31 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) } while (0) static int dib0070_set_bandwidth(struct dvb_frontend *fe) -{ - struct dib0070_state *state = fe->tuner_priv; - u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; - - if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000) - tmp |= (0 << 14); - else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000) - tmp |= (1 << 14); - else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000) - tmp |= (2 << 14); - else - tmp |= (3 << 14); - - dib0070_write_reg(state, 0x02, tmp); - - /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ - if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { - u16 value = dib0070_read_reg(state, 0x17); - - dib0070_write_reg(state, 0x17, value & 0xfffc); - tmp = dib0070_read_reg(state, 0x01) & 0x01ff; - dib0070_write_reg(state, 0x01, tmp | (60 << 9)); - - dib0070_write_reg(state, 0x17, value); - } + { + struct dib0070_state *state = fe->tuner_priv; + u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; + + if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000) + tmp |= (0 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000) + tmp |= (1 << 14); + else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); + + dib0070_write_reg(state, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(state, 0x17); + + dib0070_write_reg(state, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(state, 0x01) & 0x01ff; + dib0070_write_reg(state, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(state, 0x17, value); + } return 0; } @@ -186,7 +186,6 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state int ret = 0; if (*tune_state == CT_TUNER_STEP_0) { - dib0070_write_reg(state, 0x0f, 0xed10); dib0070_write_reg(state, 0x17, 0x0034); @@ -195,7 +194,7 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state state->adc_diff = 3000; ret = 20; - *tune_state = CT_TUNER_STEP_1; + *tune_state = CT_TUNER_STEP_1; } else if (*tune_state == CT_TUNER_STEP_1) { state->step /= 2; dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); @@ -220,9 +219,6 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff); state->adc_diff = adc; state->fcaptrim = state->captrim; - - - } state->captrim += (step_sign * state->step); @@ -243,7 +239,8 @@ static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) { struct dib0070_state *state = fe->tuner_priv; - u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + dprintk("CTRL_LO5: 0x%x", lo5); return dib0070_write_reg(state, 0x15, lo5); } @@ -257,281 +254,282 @@ void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) dib0070_write_reg(state, 0x1a, 0x0000); } else { dib0070_write_reg(state, 0x1b, 0x4112); - if (state->cfg->vga_filter != 0) { - dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); - dprintk("vga filter register is set to %x", state->cfg->vga_filter); - } else - dib0070_write_reg(state, 0x1a, 0x0009); + if (state->cfg->vga_filter != 0) { + dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); + dprintk("vga filter register is set to %x", state->cfg->vga_filter); + } else + dib0070_write_reg(state, 0x1a, 0x0009); } } EXPORT_SYMBOL(dib0070_ctrl_agc_filter); struct dib0070_tuning { - u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ - u8 switch_trim; - u8 vco_band; - u8 hfdiv; - u8 vco_multi; - u8 presc; - u8 wbdmux; - u16 tuner_enable; + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; }; struct dib0070_lna_match { - u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ - u8 lna_band; + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; }; static const struct dib0070_tuning dib0070s_tuning_table[] = { - { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ - { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, - { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, - { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ - { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, - { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, - { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ + { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ + { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ + { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ }; static const struct dib0070_tuning dib0070_tuning_table[] = { - { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ - { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ - { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, - { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, - { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ - { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 }, - { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, - { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ + { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ + { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ + { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, + { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, + { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ + { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ }; static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { - { 180000, 0 }, /* VHF */ - { 188000, 1 }, - { 196400, 2 }, - { 250000, 3 }, - { 550000, 0 }, /* UHF */ - { 590000, 1 }, - { 666000, 3 }, - { 864000, 5 }, - { 1500000, 0 }, /* LBAND or everything higher than UHF */ - { 1600000, 1 }, - { 2000000, 3 }, - { 0xffffffff, 7 }, + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 0 }, /* UHF */ + { 590000, 1 }, + { 666000, 3 }, + { 864000, 5 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, }; static const struct dib0070_lna_match dib0070_lna[] = { - { 180000, 0 }, /* VHF */ - { 188000, 1 }, - { 196400, 2 }, - { 250000, 3 }, - { 550000, 2 }, /* UHF */ - { 650000, 3 }, - { 750000, 5 }, - { 850000, 6 }, - { 864000, 7 }, - { 1500000, 0 }, /* LBAND or everything higher than UHF */ - { 1600000, 1 }, - { 2000000, 3 }, - { 0xffffffff, 7 }, + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 2 }, /* UHF */ + { 650000, 3 }, + { 750000, 5 }, + { 850000, 6 }, + { 864000, 7 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, }; #define LPF 100 static int dib0070_tune_digital(struct dvb_frontend *fe) { - struct dib0070_state *state = fe->tuner_priv; + struct dib0070_state *state = fe->tuner_priv; - const struct dib0070_tuning *tune; - const struct dib0070_lna_match *lna_match; + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; - enum frontend_tune_state *tune_state = &state->tune_state; - int ret = 10; /* 1ms is the default delay most of the time */ + enum frontend_tune_state *tune_state = &state->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ - u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000); - u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); + u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000); + u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); #ifdef CONFIG_SYS_ISDBT - if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) - if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) - && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) - || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) - || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) - && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) - freq += 850; + if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) + if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) + freq += 850; #endif - if (state->current_rf != freq) { - - switch (state->revision) { - case DIB0070S_P1A: - tune = dib0070s_tuning_table; - lna_match = dib0070_lna; - break; - default: - tune = dib0070_tuning_table; - if (state->cfg->flip_chip) - lna_match = dib0070_lna_flip_chip; - else - lna_match = dib0070_lna; - break; - } - while (freq > tune->max_freq) /* find the right one */ - tune++; - while (freq > lna_match->max_freq) /* find the right one */ - lna_match++; - - state->current_tune_table_index = tune; - state->lna_match = lna_match; - } - - if (*tune_state == CT_TUNER_START) { - dprintk("Tuning for Band: %hd (%d kHz)", band, freq); if (state->current_rf != freq) { - u8 REFDIV; - u32 FBDiv, Rest, FREF, VCOF_kHz; - u8 Den; - - state->current_rf = freq; - state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); - - - dib0070_write_reg(state, 0x17, 0x30); - - - VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; - - switch (band) { - case BAND_VHF: - REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); - break; - case BAND_FM: - REFDIV = (u8) ((state->cfg->clock_khz) / 1000); - break; - default: - REFDIV = (u8) (state->cfg->clock_khz / 10000); - break; - } - FREF = state->cfg->clock_khz / REFDIV; - - switch (state->revision) { case DIB0070S_P1A: - FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); - Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; - break; - - case DIB0070_P1G: - case DIB0070_P1F: + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; + break; default: - FBDiv = (freq / (FREF / 2)); - Rest = 2 * freq - FBDiv * FREF; - break; - } - - if (Rest < LPF) - Rest = 0; - else if (Rest < 2 * LPF) - Rest = 2 * LPF; - else if (Rest > (FREF - LPF)) { - Rest = 0; - FBDiv += 1; - } else if (Rest > (FREF - 2 * LPF)) - Rest = FREF - 2 * LPF; - Rest = (Rest * 6528) / (FREF / 10); - - Den = 1; - if (Rest > 0) { - state->lo4 |= (1 << 14) | (1 << 12); - Den = 255; + tune = dib0070_tuning_table; + if (state->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; + break; } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; + state->current_tune_table_index = tune; + state->lna_match = lna_match; + } - dib0070_write_reg(state, 0x11, (u16)FBDiv); - dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); - dib0070_write_reg(state, 0x13, (u16) Rest); - - if (state->revision == DIB0070S_P1A) { - - if (band == BAND_SBAND) { - dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); - dib0070_write_reg(state, 0x1d, 0xFFFF); - } else - dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + if (*tune_state == CT_TUNER_START) { + dprintk("Tuning for Band: %hd (%d kHz)", band, freq); + if (state->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; + + state->current_rf = freq; + state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); + + + dib0070_write_reg(state, 0x17, 0x30); + + + VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; + + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((state->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) (state->cfg->clock_khz / 10000); + break; + } + FREF = state->cfg->clock_khz / REFDIV; + + + + switch (state->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; + break; + + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } + + if (Rest < LPF) + Rest = 0; + else if (Rest < 2 * LPF) + Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { + Rest = 0; + FBDiv += 1; + } else if (Rest > (FREF - 2 * LPF)) + Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); + + Den = 1; + if (Rest > 0) { + state->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + + + dib0070_write_reg(state, 0x11, (u16)FBDiv); + dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(state, 0x13, (u16) Rest); + + if (state->revision == DIB0070S_P1A) { + + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(state, 0x1d, 0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } + + dib0070_write_reg(state, 0x20, + 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); + + dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); + dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); + dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); + dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); + dprintk("VCO = %hd", state->current_tune_table_index->vco_band); + dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); + + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { - dib0070_write_reg(state, 0x20, - 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); - - dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); - dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); - dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); - dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); - dprintk("VCO = %hd", state->current_tune_table_index->vco_band); - dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); - - *tune_state = CT_TUNER_STEP_0; - } else { /* we are already tuned to this frequency - the configuration is correct */ - ret = 50; /* wakeup time */ - *tune_state = CT_TUNER_STEP_5; - } - } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { - - ret = dib0070_captrim(state, tune_state); + ret = dib0070_captrim(state, tune_state); - } else if (*tune_state == CT_TUNER_STEP_4) { - const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; - if (tmp != NULL) { - while (freq/1000 > tmp->freq) /* find the right one */ - tmp++; - dib0070_write_reg(state, 0x0f, - (0 << 15) | (1 << 14) | (3 << 12) - | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) - | (state->current_tune_table_index->wbdmux << 0)); - state->wbd_gain_current = tmp->wbd_gain_val; - } else { + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; dib0070_write_reg(state, 0x0f, - (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index-> - wbdmux << 0)); - state->wbd_gain_current = 6; - } + (0 << 15) | (1 << 14) | (3 << 12) + | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) + | (state->current_tune_table_index->wbdmux << 0)); + state->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(state, 0x0f, + (0 << 15) | (1 << 14) | (3 << 12) + | (6 << 9) | (0 << 8) | (1 << 7) + | (state->current_tune_table_index->wbdmux << 0)); + state->wbd_gain_current = 6; + } - dib0070_write_reg(state, 0x06, 0x3fff); + dib0070_write_reg(state, 0x06, 0x3fff); dib0070_write_reg(state, 0x07, (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); - dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); - dib0070_write_reg(state, 0x0d, 0x0d80); + dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); + dib0070_write_reg(state, 0x0d, 0x0d80); - dib0070_write_reg(state, 0x18, 0x07ff); - dib0070_write_reg(state, 0x17, 0x0033); + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x17, 0x0033); - *tune_state = CT_TUNER_STEP_5; - } else if (*tune_state == CT_TUNER_STEP_5) { - dib0070_set_bandwidth(fe); - *tune_state = CT_TUNER_STOP; - } else { - ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ - } - return ret; + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe); + *tune_state = CT_TUNER_STOP; + } else { + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ + } + return ret; } static int dib0070_tune(struct dvb_frontend *fe) { - struct dib0070_state *state = fe->tuner_priv; - uint32_t ret; + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; - state->tune_state = CT_TUNER_START; + state->tune_state = CT_TUNER_START; - do { - ret = dib0070_tune_digital(fe); - if (ret != FE_CALLBACK_TIME_NEVER) - msleep(ret/10); - else - break; - } while (state->tune_state != CT_TUNER_STOP); + do { + ret = dib0070_tune_digital(fe); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret/10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); - return 0; + return 0; } static int dib0070_wakeup(struct dvb_frontend *fe) @@ -610,48 +608,48 @@ static const u16 dib0070_p1f_defaults[] = static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) { - u16 tuner_en = dib0070_read_reg(state, 0x20); - u16 offset; - - dib0070_write_reg(state, 0x18, 0x07ff); - dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); - dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); - msleep(9); - offset = dib0070_read_reg(state, 0x19); - dib0070_write_reg(state, 0x20, tuner_en); - return offset; + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); + msleep(9); + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; } static void dib0070_wbd_offset_calibration(struct dib0070_state *state) { - u8 gain; - for (gain = 6; gain < 8; gain++) { - state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); - dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); - } + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); + } } u16 dib0070_wbd_offset(struct dvb_frontend *fe) { - struct dib0070_state *state = fe->tuner_priv; - const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; - u32 freq = fe->dtv_property_cache.frequency/1000; - - if (tmp != NULL) { - while (freq/1000 > tmp->freq) /* find the right one */ - tmp++; - state->wbd_gain_current = tmp->wbd_gain_val; + struct dib0070_state *state = fe->tuner_priv; + const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; + u32 freq = fe->dtv_property_cache.frequency/1000; + + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; + state->wbd_gain_current = tmp->wbd_gain_val; } else - state->wbd_gain_current = 6; + state->wbd_gain_current = 6; - return state->wbd_offset_3_3[state->wbd_gain_current - 6]; + return state->wbd_offset_3_3[state->wbd_gain_current - 6]; } EXPORT_SYMBOL(dib0070_wbd_offset); #define pgm_read_word(w) (*w) static int dib0070_reset(struct dvb_frontend *fe) { - struct dib0070_state *state = fe->tuner_priv; + struct dib0070_state *state = fe->tuner_priv; u16 l, r, *n; HARD_RESET(state); @@ -664,7 +662,7 @@ static int dib0070_reset(struct dvb_frontend *fe) #else #warning forcing SBAND #endif - state->revision = DIB0070S_P1A; + state->revision = DIB0070S_P1A; /* P1F or not */ dprintk("Revision: %x", state->revision); @@ -703,24 +701,25 @@ static int dib0070_reset(struct dvb_frontend *fe) dib0070_write_reg(state, 0x02, r | (1 << 5)); } - if (state->revision == DIB0070S_P1A) - dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); - else - dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); + if (state->revision == DIB0070S_P1A) + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + else + dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, + state->cfg->enable_third_order_filter); dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); - dib0070_wbd_offset_calibration(state); + dib0070_wbd_offset_calibration(state); - return 0; + return 0; } static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct dib0070_state *state = fe->tuner_priv; + struct dib0070_state *state = fe->tuner_priv; - *frequency = 1000 * state->current_rf; - return 0; + *frequency = 1000 * state->current_rf; + return 0; } static int dib0070_release(struct dvb_frontend *fe) diff --git a/kernel/drivers/media/dvb-frontends/dib0090.c b/kernel/drivers/media/dvb-frontends/dib0090.c index 68e2af265..47cb72243 100644 --- a/kernel/drivers/media/dvb-frontends/dib0090.c +++ b/kernel/drivers/media/dvb-frontends/dib0090.c @@ -1696,12 +1696,10 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front if (state->identity.p1g) state->dc = dc_p1g_table; - *tune_state = CT_TUNER_STEP_0; /* fall through */ - case CT_TUNER_STEP_0: - dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q"); + dprintk("Start/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q"); dib0090_write_reg(state, 0x01, state->dc->bb1); dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7)); diff --git a/kernel/drivers/media/dvb-frontends/dib3000mb.c b/kernel/drivers/media/dvb-frontends/dib3000mb.c index af91e0c92..7a61172d0 100644 --- a/kernel/drivers/media/dvb-frontends/dib3000mb.c +++ b/kernel/drivers/media/dvb-frontends/dib3000mb.c @@ -118,7 +118,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner) { struct dib3000_state* state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - fe_code_rate_t fe_cr = FEC_NONE; + enum fe_code_rate fe_cr = FEC_NONE; int search_state, seq; if (tuner && fe->ops.tuner_ops.set_params) { @@ -454,7 +454,7 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dib3000_state* state = fe->demodulator_priv; - fe_code_rate_t *cr; + enum fe_code_rate *cr; u16 tps_val; int inv_test1,inv_test2; u32 dds_val, threshold = 0x800000; @@ -611,7 +611,8 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe) return 0; } -static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat) +static int dib3000mb_read_status(struct dvb_frontend *fe, + enum fe_status *stat) { struct dib3000_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/dib3000mc.c b/kernel/drivers/media/dvb-frontends/dib3000mc.c index ffad181a9..583d6b7fa 100644 --- a/kernel/drivers/media/dvb-frontends/dib3000mc.c +++ b/kernel/drivers/media/dvb-frontends/dib3000mc.c @@ -131,7 +131,7 @@ static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state) { u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb; - if (state->cfg->pwm3_inversion) { + if (state->cfg->pwm3_inversion) { reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0); reg_52 |= (1 << 2); } else { @@ -141,12 +141,12 @@ static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state) dib3000mc_write_word(state, 51, reg_51); dib3000mc_write_word(state, 52, reg_52); - if (state->cfg->use_pwm3) + if (state->cfg->use_pwm3) dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0)); else dib3000mc_write_word(state, 245, 0); - dib3000mc_write_word(state, 1040, 0x3); + dib3000mc_write_word(state, 1040, 0x3); return 0; } @@ -417,7 +417,7 @@ static int dib3000mc_sleep(struct dvb_frontend *demod) dib3000mc_write_word(state, 1032, 0xFFFF); dib3000mc_write_word(state, 1033, 0xFFF0); - return 0; + return 0; } static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam) @@ -447,10 +447,14 @@ static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, dib3000mc_set_bandwidth(state, bw); dib3000mc_set_timing(state, ch->transmission_mode, bw, 0); -// if (boost) -// dib3000mc_write_word(state, 100, (11 << 6) + 6); -// else +#if 1 + dib3000mc_write_word(state, 100, (16 << 6) + 9); +#else + if (boost) + dib3000mc_write_word(state, 100, (11 << 6) + 6); + else dib3000mc_write_word(state, 100, (16 << 6) + 9); +#endif dib3000mc_write_word(state, 1027, 0x0800); dib3000mc_write_word(state, 1027, 0x0000); @@ -732,7 +736,7 @@ static int dib3000mc_set_frontend(struct dvb_frontend *fe) return ret; } -static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat) +static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat) { struct dib3000mc_state *state = fe->demodulator_priv; u16 lock = dib3000mc_read_word(state, 509); diff --git a/kernel/drivers/media/dvb-frontends/dib7000m.c b/kernel/drivers/media/dvb-frontends/dib7000m.c index dcb9a15ef..35eb71fe3 100644 --- a/kernel/drivers/media/dvb-frontends/dib7000m.c +++ b/kernel/drivers/media/dvb-frontends/dib7000m.c @@ -1256,7 +1256,7 @@ static int dib7000m_set_frontend(struct dvb_frontend *fe) return ret; } -static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) +static int dib7000m_read_status(struct dvb_frontend *fe, enum fe_status *stat) { struct dib7000m_state *state = fe->demodulator_priv; u16 lock = dib7000m_read_word(state, 535); diff --git a/kernel/drivers/media/dvb-frontends/dib7000p.c b/kernel/drivers/media/dvb-frontends/dib7000p.c index c505d696f..33be5d6b9 100644 --- a/kernel/drivers/media/dvb-frontends/dib7000p.c +++ b/kernel/drivers/media/dvb-frontends/dib7000p.c @@ -1558,9 +1558,9 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe) return ret; } -static int dib7000p_get_stats(struct dvb_frontend *fe, fe_status_t stat); +static int dib7000p_get_stats(struct dvb_frontend *fe, enum fe_status stat); -static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) +static int dib7000p_read_status(struct dvb_frontend *fe, enum fe_status *stat) { struct dib7000p_state *state = fe->demodulator_priv; u16 lock = dib7000p_read_word(state, 509); @@ -1877,7 +1877,7 @@ static u32 dib7000p_get_time_us(struct dvb_frontend *demod) return time_us; } -static int dib7000p_get_stats(struct dvb_frontend *demod, fe_status_t stat) +static int dib7000p_get_stats(struct dvb_frontend *demod, enum fe_status stat) { struct dib7000p_state *state = demod->demodulator_priv; struct dtv_frontend_properties *c = &demod->dtv_property_cache; diff --git a/kernel/drivers/media/dvb-frontends/dib8000.c b/kernel/drivers/media/dvb-frontends/dib8000.c index 8c6663b63..94c26270f 100644 --- a/kernel/drivers/media/dvb-frontends/dib8000.c +++ b/kernel/drivers/media/dvb-frontends/dib8000.c @@ -3380,13 +3380,13 @@ static int dib8000_sleep(struct dvb_frontend *fe) return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); } -static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat); +static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat); static int dib8000_get_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; u16 i, val = 0; - fe_status_t stat = 0; + enum fe_status stat = 0; u8 index_frontend, sub_index_frontend; fe->dtv_property_cache.bandwidth_hz = 6000000; @@ -3733,9 +3733,9 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) return 0; } -static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat); +static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat); -static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat) { struct dib8000_state *state = fe->demodulator_priv; u16 lock_slave = 0, lock; @@ -4089,7 +4089,7 @@ static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer) return time_us; } -static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat) +static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat) { struct dib8000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache; diff --git a/kernel/drivers/media/dvb-frontends/dib8000.h b/kernel/drivers/media/dvb-frontends/dib8000.h index 780c37bdc..2b8b4b165 100644 --- a/kernel/drivers/media/dvb-frontends/dib8000.h +++ b/kernel/drivers/media/dvb-frontends/dib8000.h @@ -66,7 +66,7 @@ struct dib8000_ops { #if IS_REACHABLE(CONFIG_DVB_DIB8000) void *dib8000_attach(struct dib8000_ops *ops); #else -static inline int dib8000_attach(struct dib8000_ops *ops) +static inline void *dib8000_attach(struct dib8000_ops *ops) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/kernel/drivers/media/dvb-frontends/dib9000.c b/kernel/drivers/media/dvb-frontends/dib9000.c index f75dec443..8f92aca0b 100644 --- a/kernel/drivers/media/dvb-frontends/dib9000.c +++ b/kernel/drivers/media/dvb-frontends/dib9000.c @@ -1893,7 +1893,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) { struct dib9000_state *state = fe->demodulator_priv; u8 index_frontend, sub_index_frontend; - fe_status_t stat; + enum fe_status stat; int ret = 0; if (state->get_frontend_internal == 0) { @@ -2161,7 +2161,7 @@ static u16 dib9000_read_lock(struct dvb_frontend *fe) return dib9000_read_word(state, 535); } -static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +static int dib9000_read_status(struct dvb_frontend *fe, enum fe_status *stat) { struct dib9000_state *state = fe->demodulator_priv; u8 index_frontend; diff --git a/kernel/drivers/media/dvb-frontends/drx39xyj/drxj.c b/kernel/drivers/media/dvb-frontends/drx39xyj/drxj.c index 2bfa7a435..b28b5787b 100644 --- a/kernel/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/kernel/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -210,7 +210,7 @@ DEFINES /** * \def DRXJ_DEF_I2C_ADDR -* \brief Default I2C addres of a demodulator instance. +* \brief Default I2C address of a demodulator instance. */ #define DRXJ_DEF_I2C_ADDR (0x52) @@ -336,7 +336,7 @@ DEFINES * MICROCODE RELATED DEFINES */ -/* Magic word for checking correct Endianess of microcode data */ +/* Magic word for checking correct Endianness of microcode data */ #define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L')) /* CRC flag in ucode header, flags field. */ @@ -847,9 +847,9 @@ static struct drx_common_attr drxj_default_comm_attr_g = { static clockrate is selected */ DRX_MPEG_STR_WIDTH_1 /* MPEG Start width in clock cycles */ }, - /* Initilisations below can be ommited, they require no user input and + /* Initilisations below can be omitted, they require no user input and are initialy 0, NULL or false. The compiler will initialize them to these - values when ommited. */ + values when omitted. */ false, /* is_opened */ /* SCAN */ @@ -1175,7 +1175,7 @@ static u32 log1_times100(u32 x) Now x has binary point between bit[scale] and bit[scale-1] and 1.0 <= x < 2.0 */ - /* correction for divison: log(x) = log(x/y)+log(y) */ + /* correction for division: log(x) = log(x/y)+log(y) */ y = k * ((((u32) 1) << scale) * 200); /* remove integer part */ @@ -1653,7 +1653,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, sequense will be visible: (1) write address {i2c addr, 4 bytes chip address} (2) write data {i2c addr, 4 bytes data } (3) write address (4) write data etc... - Addres must be rewriten because HI is reset after data transport and + Address must be rewriten because HI is reset after data transport and expects an address. */ todo = (block_size < datasize ? block_size : datasize); @@ -2971,7 +2971,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o } /* ext_attr->standard */ } - if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> clear ipr_mode[0] */ + if (cfg_data->enable_parallel == true) { /* MPEG data output is parallel -> clear ipr_mode[0] */ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); } else { /* MPEG data output is serial -> set ipr_mode[0] */ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M; @@ -3157,7 +3157,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o pr_err("error %d\n", rc); goto rw_error; } - if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> set MD1 to MD7 to output mode */ + if (cfg_data->enable_parallel == true) { /* MPEG data output is parallel -> set MD1 to MD7 to output mode */ sio_pdr_md_cfg = MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH << SIO_PDR_MD0_CFG_DRIVE__B | 0x03 << @@ -4320,7 +4320,7 @@ static int adc_synchronization(struct drx_demod_instance *demod) } if (count == 1) { - /* Try sampling on a diffrent edge */ + /* Try sampling on a different edge */ u16 clk_neg = 0; rc = drxj_dap_read_reg16(dev_addr, IQM_AF_CLKNEG__A, &clk_neg, 0); @@ -6461,7 +6461,7 @@ set_qam_measurement(struct drx_demod_instance *demod, enum drx_modulation constellation, u32 symbol_rate) { struct i2c_device_addr *dev_addr = NULL; /* device address for I2C writes */ - struct drxj_data *ext_attr = NULL; /* Global data container for DRXJ specif data */ + struct drxj_data *ext_attr = NULL; /* Global data container for DRXJ specific data */ int rc; u32 fec_bits_desired = 0; /* BER accounting period */ u16 fec_rs_plen = 0; /* defines RS BER measurement period */ @@ -8864,7 +8864,7 @@ qam64auto(struct drx_demod_instance *demod, u32 timeout_ofs = 0; u16 data = 0; - /* external attributes for storing aquired channel constellation */ + /* external attributes for storing acquired channel constellation */ *lock_status = DRX_NOT_LOCKED; start_time = jiffies_to_msecs(jiffies); lck_state = NO_LOCK; @@ -9011,7 +9011,7 @@ qam256auto(struct drx_demod_instance *demod, u32 d_locked_time = 0; u32 timeout_ofs = DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; - /* external attributes for storing aquired channel constellation */ + /* external attributes for storing acquired channel constellation */ *lock_status = DRX_NOT_LOCKED; start_time = jiffies_to_msecs(jiffies); lck_state = NO_LOCK; @@ -9087,7 +9087,7 @@ set_qam_channel(struct drx_demod_instance *demod, enum drx_lock_status lock_status = DRX_NOT_LOCKED; bool auto_flag = false; - /* external attributes for storing aquired channel constellation */ + /* external attributes for storing acquired channel constellation */ ext_attr = (struct drxj_data *) demod->my_ext_attr; /* set QAM channel constellation */ @@ -9431,7 +9431,7 @@ rw_error: /** * \fn int ctrl_get_qam_sig_quality() -* \brief Retreive QAM signal quality from device. +* \brief Retrieve QAM signal quality from device. * \param devmod Pointer to demodulator instance. * \param sig_quality Pointer to signal quality data. * \return int. @@ -9541,7 +9541,7 @@ ctrl_get_qam_sig_quality(struct drx_demod_instance *demod) /* ----------------------------------------- */ /* Pre Viterbi Symbol Error Rate Calculation */ /* ----------------------------------------- */ - /* pre viterbi SER is good if it is bellow 0.025 */ + /* pre viterbi SER is good if it is below 0.025 */ /* get the register value */ /* no of quadrature symbol errors */ @@ -10647,7 +10647,7 @@ rw_error: /** * \fn int ctrl_sig_quality() -* \brief Retreive signal quality form device. +* \brief Retrieve signal quality form device. * \param devmod Pointer to demodulator instance. * \param sig_quality Pointer to signal quality data. * \return int. @@ -10763,7 +10763,7 @@ rw_error: /** * \fn int ctrl_lock_status() -* \brief Retreive lock status . +* \brief Retrieve lock status . * \param dev_addr Pointer to demodulator device address. * \param lock_stat Pointer to lock status structure. * \return int. @@ -10815,7 +10815,7 @@ ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_st return -EIO; } - /* define the SCU command paramters and execute the command */ + /* define the SCU command parameters and execute the command */ cmd_scu.parameter_len = 0; cmd_scu.result_len = 2; cmd_scu.parameter = NULL; @@ -11489,7 +11489,7 @@ static int drxj_open(struct drx_demod_instance *demod) } /* Stamp driver version number in SCU data RAM in BCD code - Done to enable field application engineers to retreive drxdriver version + Done to enable field application engineers to retrieve drxdriver version via I2C from SCU RAM */ driver_version = (VERSION_MAJOR / 100) % 10; @@ -11892,7 +11892,7 @@ release: return rc; } -/* caller is expeced to check if lna is supported before enabling */ +/* caller is expected to check if lna is supported before enabling */ static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state) { struct drxuio_cfg uio_cfg; @@ -11946,7 +11946,7 @@ static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable) return 0; } -static int drx39xxj_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int drx39xxj_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct drx39xxj_state *state = fe->demodulator_priv; struct drx_demod_instance *demod = state->demod; diff --git a/kernel/drivers/media/dvb-frontends/drxd_hard.c b/kernel/drivers/media/dvb-frontends/drxd_hard.c index 687e893d2..445a15c27 100644 --- a/kernel/drivers/media/dvb-frontends/drxd_hard.c +++ b/kernel/drivers/media/dvb-frontends/drxd_hard.c @@ -2805,7 +2805,7 @@ static int drxd_read_signal_strength(struct dvb_frontend *fe, u16 * strength) return 0; } -static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status) +static int drxd_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct drxd_state *state = fe->demodulator_priv; u32 lock; @@ -2950,10 +2950,9 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, { struct drxd_state *state = NULL; - state = kmalloc(sizeof(struct drxd_state), GFP_KERNEL); + state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL; - memset(state, 0, sizeof(*state)); state->ops = drxd_ops; state->dev = dev; diff --git a/kernel/drivers/media/dvb-frontends/drxk_hard.c b/kernel/drivers/media/dvb-frontends/drxk_hard.c index d46cf5f7c..b975da099 100644 --- a/kernel/drivers/media/dvb-frontends/drxk_hard.c +++ b/kernel/drivers/media/dvb-frontends/drxk_hard.c @@ -544,7 +544,7 @@ error: static int init_state(struct drxk_state *state) { /* - * FIXME: most (all?) of the values bellow should be moved into + * FIXME: most (all?) of the values below should be moved into * struct drxk_config, as they are probably board-specific */ u32 ul_vsb_if_agc_mode = DRXK_AGC_CTRL_AUTO; @@ -3262,6 +3262,7 @@ static int dvbt_sc_command(struct drxk_state *state, } /* Write needed parameters and the command */ + status = 0; switch (cmd) { /* All commands using 5 parameters */ /* All commands using 4 parameters */ @@ -3270,16 +3271,16 @@ static int dvbt_sc_command(struct drxk_state *state, case OFDM_SC_RA_RAM_CMD_PROC_START: case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: - status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); + status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); /* All commands using 1 parameters */ 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); + status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); /* All commands using 0 parameters */ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: case OFDM_SC_RA_RAM_CMD_NULL: /* Write command */ - status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd); + status |= write16(state, OFDM_SC_RA_RAM_CMD__A, cmd); break; default: /* Unknown command */ @@ -6639,7 +6640,7 @@ error: } -static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int drxk_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct drxk_state *state = fe->demodulator_priv; int rc; diff --git a/kernel/drivers/media/dvb-frontends/drxk_hard.h b/kernel/drivers/media/dvb-frontends/drxk_hard.h index bae9c71dc..9ed88e014 100644 --- a/kernel/drivers/media/dvb-frontends/drxk_hard.h +++ b/kernel/drivers/media/dvb-frontends/drxk_hard.h @@ -350,7 +350,7 @@ struct drxk_state { bool antenna_dvbt; u16 antenna_gpio; - fe_status_t fe_status; + enum fe_status fe_status; /* Firmware */ const char *microcode_name; diff --git a/kernel/drivers/media/dvb-frontends/ds3000.c b/kernel/drivers/media/dvb-frontends/ds3000.c index 9d0d03477..e8fc0329e 100644 --- a/kernel/drivers/media/dvb-frontends/ds3000.c +++ b/kernel/drivers/media/dvb-frontends/ds3000.c @@ -404,7 +404,8 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, return ret; } -static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int ds3000_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct ds3000_state *state = fe->demodulator_priv; u8 data; @@ -431,7 +432,7 @@ static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } -static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) +static int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct ds3000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; @@ -666,7 +667,7 @@ static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return 0; } -static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int ds3000_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct ds3000_state *state = fe->demodulator_priv; u8 data; @@ -766,7 +767,7 @@ static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, /* Send DiSEqC burst */ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct ds3000_state *state = fe->demodulator_priv; int i; @@ -905,7 +906,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i; - fe_status_t status; + enum fe_status status; s32 offset_khz; u32 frequency; u16 value; @@ -1045,7 +1046,7 @@ static int ds3000_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { if (re_tune) { int ret = ds3000_set_frontend(fe); diff --git a/kernel/drivers/media/dvb-frontends/dvb-pll.c b/kernel/drivers/media/dvb-frontends/dvb-pll.c index 6d8fe8843..53089e142 100644 --- a/kernel/drivers/media/dvb-frontends/dvb-pll.c +++ b/kernel/drivers/media/dvb-frontends/dvb-pll.c @@ -34,7 +34,7 @@ struct dvb_pll_priv { struct i2c_adapter *i2c; /* the PLL descriptor */ - struct dvb_pll_desc *pll_desc; + const struct dvb_pll_desc *pll_desc; /* cached frequency/bandwidth */ u32 frequency; @@ -57,7 +57,7 @@ MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)"); /* ----------------------------------------------------------- */ struct dvb_pll_desc { - char *name; + const char *name; u32 min; u32 max; u32 iffreq; @@ -71,13 +71,13 @@ struct dvb_pll_desc { u32 stepsize; u8 config; u8 cb; - } entries[12]; + } entries[]; }; /* ----------------------------------------------------------- */ /* descriptions */ -static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { .name = "Thomson dtt7579", .min = 177000000, .max = 858000000, @@ -99,7 +99,7 @@ static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x10; } -static struct dvb_pll_desc dvb_pll_thomson_dtt759x = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = { .name = "Thomson dtt759x", .min = 177000000, .max = 896000000, @@ -123,7 +123,7 @@ static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] ^= 0x10; } -static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { .name = "Thomson dtt7520x", .min = 185000000, .max = 900000000, @@ -141,7 +141,7 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { }, }; -static struct dvb_pll_desc dvb_pll_lg_z201 = { +static const struct dvb_pll_desc dvb_pll_lg_z201 = { .name = "LG z201", .min = 174000000, .max = 862000000, @@ -157,7 +157,7 @@ static struct dvb_pll_desc dvb_pll_lg_z201 = { }, }; -static struct dvb_pll_desc dvb_pll_unknown_1 = { +static const struct dvb_pll_desc dvb_pll_unknown_1 = { .name = "unknown 1", /* used by dntv live dvb-t */ .min = 174000000, .max = 862000000, @@ -179,7 +179,7 @@ static struct dvb_pll_desc dvb_pll_unknown_1 = { /* Infineon TUA6010XS * used in Thomson Cable Tuner */ -static struct dvb_pll_desc dvb_pll_tua6010xs = { +static const struct dvb_pll_desc dvb_pll_tua6010xs = { .name = "Infineon TUA6010XS", .min = 44250000, .max = 858000000, @@ -193,7 +193,7 @@ static struct dvb_pll_desc dvb_pll_tua6010xs = { }; /* Panasonic env57h1xd5 (some Philips PLL ?) */ -static struct dvb_pll_desc dvb_pll_env57h1xd5 = { +static const struct dvb_pll_desc dvb_pll_env57h1xd5 = { .name = "Panasonic ENV57H1XD5", .min = 44250000, .max = 858000000, @@ -217,7 +217,7 @@ static void tda665x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x08; } -static struct dvb_pll_desc dvb_pll_tda665x = { +static const struct dvb_pll_desc dvb_pll_tda665x = { .name = "Philips TDA6650/TDA6651", .min = 44250000, .max = 858000000, @@ -251,7 +251,7 @@ static void tua6034_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x08; } -static struct dvb_pll_desc dvb_pll_tua6034 = { +static const struct dvb_pll_desc dvb_pll_tua6034 = { .name = "Infineon TUA6034", .min = 44250000, .max = 858000000, @@ -275,7 +275,7 @@ static void tded4_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x04; } -static struct dvb_pll_desc dvb_pll_tded4 = { +static const struct dvb_pll_desc dvb_pll_tded4 = { .name = "ALPS TDED4", .min = 47000000, .max = 863000000, @@ -293,7 +293,7 @@ static struct dvb_pll_desc dvb_pll_tded4 = { /* ALPS TDHU2 * used in AverTVHD MCE A180 */ -static struct dvb_pll_desc dvb_pll_tdhu2 = { +static const struct dvb_pll_desc dvb_pll_tdhu2 = { .name = "ALPS TDHU2", .min = 54000000, .max = 864000000, @@ -310,7 +310,7 @@ static struct dvb_pll_desc dvb_pll_tdhu2 = { /* Samsung TBMV30111IN / TBMV30712IN1 * used in Air2PC ATSC - 2nd generation (nxt2002) */ -static struct dvb_pll_desc dvb_pll_samsung_tbmv = { +static const struct dvb_pll_desc dvb_pll_samsung_tbmv = { .name = "Samsung TBMV30111IN / TBMV30712IN1", .min = 54000000, .max = 860000000, @@ -329,7 +329,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmv = { /* * Philips SD1878 Tuner. */ -static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { +static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { .name = "Philips SD1878", .min = 950000, .max = 2150000, @@ -395,7 +395,7 @@ static void opera1_bw(struct dvb_frontend *fe, u8 *buf) return; } -static struct dvb_pll_desc dvb_pll_opera1 = { +static const struct dvb_pll_desc dvb_pll_opera1 = { .name = "Opera Tuner", .min = 900000, .max = 2250000, @@ -442,7 +442,7 @@ static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf) } /* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ -static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { +static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { .name = "Samsung DTOS403IH102A", .min = 44250000, .max = 858000000, @@ -462,7 +462,7 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { }; /* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ -static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { +static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { .name = "Samsung TDTC9251DH0", .min = 48000000, .max = 863000000, @@ -476,7 +476,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { }; /* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ -static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { +static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { .name = "Samsung TBDU18132", .min = 950000, .max = 2150000, /* guesses */ @@ -497,7 +497,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { }; /* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ -static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { +static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { .name = "Samsung TBMU24112", .min = 950000, .max = 2150000, /* guesses */ @@ -518,7 +518,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { * 153 - 430 0 * 0 0 0 0 1 0 0x02 * 430 - 822 0 * 0 0 1 0 0 0 0x08 * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ -static struct dvb_pll_desc dvb_pll_alps_tdee4 = { +static const struct dvb_pll_desc dvb_pll_alps_tdee4 = { .name = "ALPS TDEE4", .min = 47000000, .max = 862000000, @@ -534,7 +534,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = { /* ----------------------------------------------------------- */ -static struct dvb_pll_desc *pll_list[] = { +static const struct dvb_pll_desc *pll_list[] = { [DVB_PLL_UNDEFINED] = NULL, [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, @@ -564,7 +564,7 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf, const u32 frequency) { struct dvb_pll_priv *priv = fe->tuner_priv; - struct dvb_pll_desc *desc = priv->pll_desc; + const struct dvb_pll_desc *desc = priv->pll_desc; u32 div; int i; @@ -758,7 +758,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, .buf = b1, .len = 1 }; struct dvb_pll_priv *priv = NULL; int ret; - struct dvb_pll_desc *desc; + const struct dvb_pll_desc *desc; if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) && (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list))) diff --git a/kernel/drivers/media/dvb-frontends/dvb_dummy_fe.c b/kernel/drivers/media/dvb-frontends/dvb_dummy_fe.c index d5acc3047..14e996d45 100644 --- a/kernel/drivers/media/dvb-frontends/dvb_dummy_fe.c +++ b/kernel/drivers/media/dvb-frontends/dvb_dummy_fe.c @@ -33,7 +33,8 @@ struct dvb_dummy_fe_state { }; -static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int dvb_dummy_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) { *status = FE_HAS_SIGNAL | FE_HAS_CARRIER @@ -97,12 +98,14 @@ static int dvb_dummy_fe_init(struct dvb_frontend* fe) return 0; } -static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int dvb_dummy_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { return 0; } -static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int dvb_dummy_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { return 0; } diff --git a/kernel/drivers/media/dvb-frontends/ec100.c b/kernel/drivers/media/dvb-frontends/ec100.c index 9d424809d..c9012e677 100644 --- a/kernel/drivers/media/dvb-frontends/ec100.c +++ b/kernel/drivers/media/dvb-frontends/ec100.c @@ -174,7 +174,7 @@ static int ec100_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int ec100_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct ec100_state *state = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/hd29l2.c b/kernel/drivers/media/dvb-frontends/hd29l2.c index 67c8e6df4..40e359f2d 100644 --- a/kernel/drivers/media/dvb-frontends/hd29l2.c +++ b/kernel/drivers/media/dvb-frontends/hd29l2.c @@ -211,7 +211,7 @@ err: return ret; } -static int hd29l2_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int hd29l2_read_status(struct dvb_frontend *fe, enum fe_status *status) { int ret; struct hd29l2_priv *priv = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/hd29l2_priv.h b/kernel/drivers/media/dvb-frontends/hd29l2_priv.h index 4d571a228..6dc225c4b 100644 --- a/kernel/drivers/media/dvb-frontends/hd29l2_priv.h +++ b/kernel/drivers/media/dvb-frontends/hd29l2_priv.h @@ -67,7 +67,7 @@ struct hd29l2_priv { struct hd29l2_config cfg; u8 tuner_i2c_addr_programmed:1; - fe_status_t fe_status; + enum fe_status fe_status; }; static const struct reg_mod_vals reg_mod_vals_tab[] = { diff --git a/kernel/drivers/media/dvb-frontends/horus3a.c b/kernel/drivers/media/dvb-frontends/horus3a.c new file mode 100644 index 000000000..000606af7 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/horus3a.c @@ -0,0 +1,430 @@ +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include "horus3a.h" +#include "dvb_frontend.h" + +#define MAX_WRITE_REGSIZE 5 + +enum horus3a_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct horus3a_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum horus3a_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +static void horus3a_i2c_debug(struct horus3a_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("horus3a: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int horus3a_write_regs(struct horus3a_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[MAX_WRITE_REGSIZE + 1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = len + 1, + .buf = buf, + } + }; + + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + + horus3a_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val) +{ + return horus3a_write_regs(priv, reg, &val, 1); +} + +static int horus3a_enter_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* MDIV_EN = 0 */ + horus3a_write_reg(priv, 0x29, 0x70); + /* VCO disable preparation */ + horus3a_write_reg(priv, 0x28, 0x3e); + /* VCO buffer disable */ + horus3a_write_reg(priv, 0x2a, 0x19); + /* VCO calibration disable */ + horus3a_write_reg(priv, 0x1c, 0x00); + /* Power save setting (xtal is not stopped) */ + data[0] = 0xC0; + /* LNA is Disabled */ + data[1] = 0xA7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + priv->state = STATE_SLEEP; + return 0; +} + +static int horus3a_leave_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + /* Leave power save */ + data[0] = 0x00; + /* LNA is Disabled */ + data[1] = 0xa7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + /* VCO buffer enable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* VCO calibration enable */ + horus3a_write_reg(priv, 0x1c, 0xc0); + /* MDIV_EN = 1 */ + horus3a_write_reg(priv, 0x29, 0x71); + usleep_range(5000, 7000); + priv->state = STATE_ACTIVE; + return 0; +} + +static int horus3a_init(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return 0; +} + +static int horus3a_release(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int horus3a_sleep(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + horus3a_enter_power_save(priv); + return 0; +} + +static int horus3a_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct horus3a_priv *priv = fe->tuner_priv; + u32 frequency = p->frequency; + u32 symbol_rate = p->symbol_rate/1000; + u8 mixdiv = 0; + u8 mdiv = 0; + u32 ms = 0; + u8 f_ctl = 0; + u8 g_ctl = 0; + u8 fc_lpf = 0; + u8 data[5]; + + dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n", + __func__, frequency, symbol_rate); + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + if (priv->state == STATE_SLEEP) + horus3a_leave_power_save(priv); + + /* frequency should be X MHz (X : integer) */ + frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000; + if (frequency <= 1155000) { + mixdiv = 4; + mdiv = 1; + } else { + mixdiv = 2; + mdiv = 0; + } + /* Assumed that fREF == 1MHz (1000kHz) */ + ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000); + if (ms > 0x7FFF) { /* 15 bit */ + dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n", + frequency); + return -EINVAL; + } + if (frequency < 975000) { + /* F_CTL=11100 G_CTL=001 */ + f_ctl = 0x1C; + g_ctl = 0x01; + } else if (frequency < 1050000) { + /* F_CTL=11000 G_CTL=010 */ + f_ctl = 0x18; + g_ctl = 0x02; + } else if (frequency < 1150000) { + /* F_CTL=10100 G_CTL=010 */ + f_ctl = 0x14; + g_ctl = 0x02; + } else if (frequency < 1250000) { + /* F_CTL=10000 G_CTL=011 */ + f_ctl = 0x10; + g_ctl = 0x03; + } else if (frequency < 1350000) { + /* F_CTL=01100 G_CTL=100 */ + f_ctl = 0x0C; + g_ctl = 0x04; + } else if (frequency < 1450000) { + /* F_CTL=01010 G_CTL=100 */ + f_ctl = 0x0A; + g_ctl = 0x04; + } else if (frequency < 1600000) { + /* F_CTL=00111 G_CTL=101 */ + f_ctl = 0x07; + g_ctl = 0x05; + } else if (frequency < 1800000) { + /* F_CTL=00100 G_CTL=010 */ + f_ctl = 0x04; + g_ctl = 0x02; + } else if (frequency < 2000000) { + /* F_CTL=00010 G_CTL=001 */ + f_ctl = 0x02; + g_ctl = 0x01; + } else { + /* F_CTL=00000 G_CTL=000 */ + f_ctl = 0x00; + g_ctl = 0x00; + } + /* LPF cutoff frequency setting */ + if (p->delivery_system == SYS_DVBS) { + /* + * rolloff = 0.35 + * SR <= 4.3 + * fc_lpf = 5 + * 4.3 < SR <= 10 + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 = + * SR * 1.175 = SR * (47/40) + * 10 < SR + * fc_lpf = SR * (1 + rolloff) / 2 + 5 = + * SR * 0.675 + 5 = SR * (27/40) + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4300) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000); + else + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 27, 40000) + 5; + /* 5 <= fc_lpf <= 36 */ + if (fc_lpf > 36) + fc_lpf = 36; + } else if (p->delivery_system == SYS_DVBS2) { + int rolloff; + + switch (p->rolloff) { + case ROLLOFF_35: + rolloff = 35; + break; + case ROLLOFF_25: + rolloff = 25; + break; + case ROLLOFF_20: + rolloff = 20; + break; + case ROLLOFF_AUTO: + default: + dev_err(&priv->i2c->dev, + "horus3a: auto roll-off is not supported\n"); + return -EINVAL; + } + /* + * SR <= 4.5: + * fc_lpf = 5 + * 4.5 < SR <= 10: + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 + * 10 < SR: + * fc_lpf = SR * (1 + rolloff) / 2 + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4500) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)DIV_ROUND_UP( + symbol_rate * (200 + rolloff), 200000); + else + fc_lpf = (u8)DIV_ROUND_UP( + symbol_rate * (100 + rolloff), 200000) + 5; + /* 5 <= fc_lpf <= 36 is valid */ + if (fc_lpf > 36) + fc_lpf = 36; + } else { + dev_err(&priv->i2c->dev, + "horus3a: invalid delivery system %d\n", + p->delivery_system); + return -EINVAL; + } + /* 0x00 - 0x04 */ + data[0] = (u8)((ms >> 7) & 0xFF); + data[1] = (u8)((ms << 1) & 0xFF); + data[2] = 0x00; + data[3] = 0x00; + data[4] = (u8)(mdiv << 7); + horus3a_write_regs(priv, 0x00, data, sizeof(data)); + /* Write G_CTL, F_CTL */ + horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl)); + /* Write LPF cutoff frequency */ + horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1))); + /* Start Calibration */ + horus3a_write_reg(priv, 0x05, 0x80); + /* IQ Generator enable */ + horus3a_write_reg(priv, 0x2a, 0x7b); + /* tuner stabilization time */ + msleep(60); + /* Store tuned frequency to the struct */ + priv->frequency = ms * 2 * 1000 / mixdiv; + return 0; +} + +static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops horus3a_tuner_ops = { + .info = { + .name = "Sony Horus3a", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 1000, + }, + .init = horus3a_init, + .release = horus3a_release, + .sleep = horus3a_sleep, + .set_params = horus3a_set_params, + .get_frequency = horus3a_get_frequency, +}; + +struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c) +{ + u8 buf[3], val; + struct horus3a_priv *priv = NULL; + + priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* wait 4ms after power on */ + usleep_range(4000, 6000); + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* REF_R = Xtal Frequency */ + buf[0] = config->xtal_freq_mhz; + buf[1] = config->xtal_freq_mhz; + buf[2] = 0; + /* 0x6 - 0x8 */ + horus3a_write_regs(priv, 0x6, buf, 3); + /* IQ Out = Single Ended */ + horus3a_write_reg(priv, 0x0a, 0x40); + switch (config->xtal_freq_mhz) { + case 27: + val = 0x1f; + break; + case 24: + val = 0x10; + break; + case 16: + val = 0xc; + break; + default: + val = 0; + dev_warn(&priv->i2c->dev, + "horus3a: invalid xtal frequency %dMHz\n", + config->xtal_freq_mhz); + break; + } + val <<= 2; + horus3a_write_reg(priv, 0x0e, val); + horus3a_enter_power_save(priv); + usleep_range(3000, 5000); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HORUS3A attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(horus3a_attach); + +MODULE_DESCRIPTION("Sony HORUS3A sattelite tuner driver"); +MODULE_AUTHOR("Sergey Kozlov "); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/horus3a.h b/kernel/drivers/media/dvb-frontends/horus3a.h new file mode 100644 index 000000000..c1e2d1834 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/horus3a.h @@ -0,0 +1,58 @@ +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 __DVB_HORUS3A_H__ +#define __DVB_HORUS3A_H__ + +#include +#include +#include + +/** + * struct horus3a_config - the configuration of Horus3A tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct horus3a_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_HORUS3A) +extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/kernel/drivers/media/dvb-frontends/isl6405.c b/kernel/drivers/media/dvb-frontends/isl6405.c index 0c642a5bf..b46450a10 100644 --- a/kernel/drivers/media/dvb-frontends/isl6405.c +++ b/kernel/drivers/media/dvb-frontends/isl6405.c @@ -43,7 +43,8 @@ struct isl6405 { u8 i2c_addr; }; -static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int isl6405_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, diff --git a/kernel/drivers/media/dvb-frontends/isl6421.c b/kernel/drivers/media/dvb-frontends/isl6421.c index c77002fcc..3a4d4606a 100644 --- a/kernel/drivers/media/dvb-frontends/isl6421.c +++ b/kernel/drivers/media/dvb-frontends/isl6421.c @@ -43,7 +43,8 @@ struct isl6421 { u8 i2c_addr; }; -static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int isl6421_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, @@ -89,7 +90,8 @@ static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; } -static int isl6421_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int isl6421_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, diff --git a/kernel/drivers/media/dvb-frontends/l64781.c b/kernel/drivers/media/dvb-frontends/l64781.c index ddf866c46..097787123 100644 --- a/kernel/drivers/media/dvb-frontends/l64781.c +++ b/kernel/drivers/media/dvb-frontends/l64781.c @@ -359,7 +359,7 @@ static int get_frontend(struct dvb_frontend *fe) return 0; } -static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int l64781_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct l64781_state* state = fe->demodulator_priv; int sync = l64781_readreg (state, 0x32); diff --git a/kernel/drivers/media/dvb-frontends/lg2160.c b/kernel/drivers/media/dvb-frontends/lg2160.c index 99efeba3c..7880f71cc 100644 --- a/kernel/drivers/media/dvb-frontends/lg2160.c +++ b/kernel/drivers/media/dvb-frontends/lg2160.c @@ -1203,7 +1203,7 @@ static int lg216x_read_lock_status(struct lg216x_state *state, #endif } -static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int lg216x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct lg216x_state *state = fe->demodulator_priv; int ret, acq_lock, sync_lock; diff --git a/kernel/drivers/media/dvb-frontends/lgdt3305.c b/kernel/drivers/media/dvb-frontends/lgdt3305.c index d08570af1..471218661 100644 --- a/kernel/drivers/media/dvb-frontends/lgdt3305.c +++ b/kernel/drivers/media/dvb-frontends/lgdt3305.c @@ -60,7 +60,7 @@ struct lgdt3305_state { struct dvb_frontend frontend; - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 current_frequency; u32 snr; }; @@ -912,7 +912,7 @@ fail: return ret; } -static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int lgdt3305_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct lgdt3305_state *state = fe->demodulator_priv; u8 val; diff --git a/kernel/drivers/media/dvb-frontends/lgdt3306a.c b/kernel/drivers/media/dvb-frontends/lgdt3306a.c index d9a2b0e76..721fbc07e 100644 --- a/kernel/drivers/media/dvb-frontends/lgdt3306a.c +++ b/kernel/drivers/media/dvb-frontends/lgdt3306a.c @@ -62,7 +62,7 @@ struct lgdt3306a_state { struct dvb_frontend frontend; - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 current_frequency; u32 snr; }; @@ -1558,7 +1558,8 @@ lgdt3306a_qam_lock_poll(struct lgdt3306a_state *state) return LG3306_UNLOCK; } -static int lgdt3306a_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int lgdt3306a_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct lgdt3306a_state *state = fe->demodulator_priv; u16 strength = 0; @@ -1705,7 +1706,7 @@ static int lgdt3306a_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int lgdt3306a_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { int ret = 0; struct lgdt3306a_state *state = fe->demodulator_priv; @@ -1735,7 +1736,7 @@ static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe, static int lgdt3306a_search(struct dvb_frontend *fe) { - fe_status_t status = 0; + enum fe_status status = 0; int i, ret; /* set frontend */ @@ -2101,7 +2102,7 @@ static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state) lgdt3306a_read_reg(state, regtab[i], ®val1[i]); if (regval1[i] != regval2[i]) { lg_debug(" %04X = %02X\n", regtab[i], regval1[i]); - regval2[i] = regval1[i]; + regval2[i] = regval1[i]; } } debug = sav_debug; diff --git a/kernel/drivers/media/dvb-frontends/lgdt330x.c b/kernel/drivers/media/dvb-frontends/lgdt330x.c index 2e1a61893..cf3cc2051 100644 --- a/kernel/drivers/media/dvb-frontends/lgdt330x.c +++ b/kernel/drivers/media/dvb-frontends/lgdt330x.c @@ -67,7 +67,7 @@ struct lgdt330x_state struct dvb_frontend frontend; /* Demodulator private data */ - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 snr; /* Result of last SNR calculation */ /* Tuner private data */ @@ -447,7 +447,8 @@ static int lgdt330x_get_frontend(struct dvb_frontend *fe) return 0; } -static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int lgdt3302_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct lgdt330x_state* state = fe->demodulator_priv; u8 buf[3]; @@ -505,7 +506,8 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) return 0; } -static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int lgdt3303_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct lgdt330x_state* state = fe->demodulator_priv; int err; diff --git a/kernel/drivers/media/dvb-frontends/lgs8gl5.c b/kernel/drivers/media/dvb-frontends/lgs8gl5.c index 416cce3fe..7bbb2c18c 100644 --- a/kernel/drivers/media/dvb-frontends/lgs8gl5.c +++ b/kernel/drivers/media/dvb-frontends/lgs8gl5.c @@ -249,7 +249,7 @@ lgs8gl5_init(struct dvb_frontend *fe) static int -lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status) +lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct lgs8gl5_state *state = fe->demodulator_priv; u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); diff --git a/kernel/drivers/media/dvb-frontends/lgs8gxx.c b/kernel/drivers/media/dvb-frontends/lgs8gxx.c index 3c92f36ea..e2c191c8b 100644 --- a/kernel/drivers/media/dvb-frontends/lgs8gxx.c +++ b/kernel/drivers/media/dvb-frontends/lgs8gxx.c @@ -732,7 +732,8 @@ int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +static int lgs8gxx_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) { struct lgs8gxx_state *priv = fe->demodulator_priv; s8 ret; diff --git a/kernel/drivers/media/dvb-frontends/lnbh25.c b/kernel/drivers/media/dvb-frontends/lnbh25.c new file mode 100644 index 000000000..ef3021e96 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/lnbh25.c @@ -0,0 +1,189 @@ +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include + +#include "dvb_frontend.h" +#include "lnbh25.h" + +/** + * struct lnbh25_priv - LNBH25 driver private data + * @i2c: pointer to the I2C adapter structure + * @i2c_address: I2C address of LNBH25 SEC chip + * @config: Registers configuration: + * offset 0: 1st register address, always 0x02 (DATA1) + * offset 1: DATA1 register value + * offset 2: DATA2 register value + */ +struct lnbh25_priv { + struct i2c_adapter *i2c; + u8 i2c_address; + u8 config[3]; +}; + +#define LNBH25_STATUS_OFL 0x1 +#define LNBH25_STATUS_VMON 0x4 +#define LNBH25_VSEL_13 0x03 +#define LNBH25_VSEL_18 0x0a + +static int lnbh25_read_vmon(struct lnbh25_priv *priv) +{ + int i, ret; + u8 addr = 0x00; + u8 status[6]; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = &addr + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = sizeof(status), + .buf = status + } + }; + + for (i = 0; i < 2; i++) { + ret = i2c_transfer(priv->i2c, &msg[i], 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_dbg(&priv->i2c->dev, + "%s(): I2C transfer %d failed (%d)\n", + __func__, i, ret); + return ret; + } + } + print_hex_dump_bytes("lnbh25_read_vmon: ", + DUMP_PREFIX_OFFSET, status, sizeof(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", + __func__, status[0]); + return -EIO; + } + return 0; +} + +static int lnbh25_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + int ret; + u8 data1_reg; + const char *vsel; + struct lnbh25_priv *priv = fe->sec_priv; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(priv->config), + .buf = priv->config + }; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + data1_reg = 0x00; + vsel = "Off"; + break; + case SEC_VOLTAGE_13: + data1_reg = LNBH25_VSEL_13; + vsel = "13V"; + break; + case SEC_VOLTAGE_18: + data1_reg = LNBH25_VSEL_18; + vsel = "18V"; + break; + default: + return -EINVAL; + } + priv->config[1] = data1_reg; + dev_dbg(&priv->i2c->dev, + "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n", + __func__, vsel, priv->i2c_address, + priv->config[0], priv->config[1], priv->config[2]); + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n", + __func__, ret); + return ret; + } + if (voltage != SEC_VOLTAGE_OFF) { + msleep(120); + ret = lnbh25_read_vmon(priv); + } else { + msleep(20); + ret = 0; + } + return ret; +} + +static void lnbh25_release(struct dvb_frontend *fe) +{ + struct lnbh25_priv *priv = fe->sec_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF); + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + struct lnbh25_priv *priv; + + dev_dbg(&i2c->dev, "%s()\n", __func__); + priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c_address = (cfg->i2c_address >> 1); + priv->i2c = i2c; + priv->config[0] = 0x02; + priv->config[1] = 0x00; + priv->config[2] = cfg->data2_config; + fe->sec_priv = priv; + if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dev_err(&i2c->dev, + "%s(): no LNBH25 found at I2C addr 0x%02x\n", + __func__, priv->i2c_address); + kfree(priv); + fe->sec_priv = NULL; + return NULL; + } + + 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", + __func__, priv->i2c_address); + return fe; +} +EXPORT_SYMBOL(lnbh25_attach); + +MODULE_DESCRIPTION("ST LNBH25 driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/lnbh25.h b/kernel/drivers/media/dvb-frontends/lnbh25.h new file mode 100644 index 000000000..1f329ef05 --- /dev/null +++ b/kernel/drivers/media/dvb-frontends/lnbh25.h @@ -0,0 +1,56 @@ +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 LNBH25_H +#define LNBH25_H + +#include +#include +#include + +/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */ +#define LNBH25_TEN 0x01 +/* Low power mode activated (used only with 22 kHz tone output disabled) */ +#define LNBH25_LPM 0x02 +/* DSQIN input pin is set to receive external 22 kHz TTL signal source */ +#define LNBH25_EXTM 0x04 + +struct lnbh25_config { + u8 i2c_address; + u8 data2_config; +}; + +#if IS_REACHABLE(CONFIG_DVB_LNBH25) +struct dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/kernel/drivers/media/dvb-frontends/lnbp21.c b/kernel/drivers/media/dvb-frontends/lnbp21.c index f3ba7b5fa..4aca0fb9a 100644 --- a/kernel/drivers/media/dvb-frontends/lnbp21.c +++ b/kernel/drivers/media/dvb-frontends/lnbp21.c @@ -45,7 +45,7 @@ struct lnbp21 { }; static int lnbp21_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, @@ -92,7 +92,7 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) } static int lnbp21_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t tone) + enum fe_sec_tone_mode tone) { struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, diff --git a/kernel/drivers/media/dvb-frontends/lnbp22.c b/kernel/drivers/media/dvb-frontends/lnbp22.c index c463da7f6..d7ca0fdd0 100644 --- a/kernel/drivers/media/dvb-frontends/lnbp22.c +++ b/kernel/drivers/media/dvb-frontends/lnbp22.c @@ -48,7 +48,8 @@ struct lnbp22 { struct i2c_adapter *i2c; }; -static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int lnbp22_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; struct i2c_msg msg = { diff --git a/kernel/drivers/media/dvb-frontends/m88ds3103.c b/kernel/drivers/media/dvb-frontends/m88ds3103.c index d3d928e1c..feeeb70d8 100644 --- a/kernel/drivers/media/dvb-frontends/m88ds3103.c +++ b/kernel/drivers/media/dvb-frontends/m88ds3103.c @@ -1,5 +1,5 @@ /* - * Montage M88DS3103/M88RS6000 demodulator driver + * Montage Technology M88DS3103/M88RS6000 demodulator driver * * Copyright (C) 2013 Antti Palosaari * @@ -18,149 +18,36 @@ static struct dvb_frontend_ops m88ds3103_ops; -/* write multiple registers */ -static int m88ds3103_wr_regs(struct m88ds3103_priv *priv, - u8 reg, const u8 *val, int len) -{ -#define MAX_WR_LEN 32 -#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) - int ret; - u8 buf[MAX_WR_XFER_LEN]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_WR_LEN)) - return -EINVAL; - - buf[0] = reg; - memcpy(&buf[1], val, len); - - mutex_lock(&priv->i2c_mutex); - ret = i2c_transfer(priv->i2c, msg, 1); - mutex_unlock(&priv->i2c_mutex); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* read multiple registers */ -static int m88ds3103_rd_regs(struct m88ds3103_priv *priv, - u8 reg, u8 *val, int len) -{ -#define MAX_RD_LEN 3 -#define MAX_RD_XFER_LEN (MAX_RD_LEN) - int ret; - u8 buf[MAX_RD_XFER_LEN]; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg->i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_RD_LEN)) - return -EINVAL; - - mutex_lock(&priv->i2c_mutex); - ret = i2c_transfer(priv->i2c, msg, 2); - mutex_unlock(&priv->i2c_mutex); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write single register */ -static int m88ds3103_wr_reg(struct m88ds3103_priv *priv, u8 reg, u8 val) -{ - return m88ds3103_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -static int m88ds3103_rd_reg(struct m88ds3103_priv *priv, u8 reg, u8 *val) -{ - return m88ds3103_rd_regs(priv, reg, val, 1); -} - /* write single register with mask */ -static int m88ds3103_wr_reg_mask(struct m88ds3103_priv *priv, - u8 reg, u8 val, u8 mask) +static int m88ds3103_update_bits(struct m88ds3103_dev *dev, + u8 reg, u8 mask, u8 val) { int ret; - u8 u8tmp; + u8 tmp; /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1); + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); if (ret) return ret; val &= mask; - u8tmp &= ~mask; - val |= u8tmp; - } - - return m88ds3103_wr_regs(priv, reg, &val, 1); -} - -/* read single register with mask */ -static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv, - u8 reg, u8 *val, u8 mask) -{ - int ret, i; - u8 u8tmp; - - ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1); - if (ret) - return ret; - - u8tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; + tmp &= ~mask; + val |= tmp; } - *val = u8tmp >> i; - return 0; + return regmap_bulk_write(dev->regmap, reg, &val, 1); } /* write reg val table using reg addr auto increment */ -static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv, +static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev, const struct m88ds3103_reg_val *tab, int tab_len) { + struct i2c_client *client = dev->client; int ret, i, j; u8 buf[83]; - dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); + dev_dbg(&client->dev, "tab_len=%d\n", tab_len); if (tab_len > 86) { ret = -EINVAL; @@ -171,8 +58,8 @@ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv, buf[j] = tab[i].val; if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 || - !((j + 1) % (priv->cfg->i2c_wr_max - 1))) { - ret = m88ds3103_wr_regs(priv, tab[i].reg - j, buf, j + 1); + !((j + 1) % (dev->cfg->i2c_wr_max - 1))) { + ret = regmap_bulk_write(dev->regmap, tab[i].reg - j, buf, j + 1); if (ret) goto err; @@ -182,66 +69,238 @@ static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv, return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status) +/* + * Get the demodulator AGC PWM voltage setting supplied to the tuner. + */ +int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm) { - struct m88ds3103_priv *priv = fe->demodulator_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct m88ds3103_dev *dev = fe->demodulator_priv; + unsigned tmp; int ret; - u8 u8tmp; + + ret = regmap_read(dev->regmap, 0x3f, &tmp); + if (ret == 0) + *_agc_pwm = tmp; + return ret; +} +EXPORT_SYMBOL(m88ds3103_get_agc_pwm); + +static int m88ds3103_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, itmp; + unsigned int utmp; + u8 buf[3]; *status = 0; - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } switch (c->delivery_system) { case SYS_DVBS: - ret = m88ds3103_rd_reg_mask(priv, 0xd1, &u8tmp, 0x07); + ret = regmap_read(dev->regmap, 0xd1, &utmp); if (ret) goto err; - if (u8tmp == 0x07) + if ((utmp & 0x07) == 0x07) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; break; case SYS_DVBS2: - ret = m88ds3103_rd_reg_mask(priv, 0x0d, &u8tmp, 0x8f); + ret = regmap_read(dev->regmap, 0x0d, &utmp); if (ret) goto err; - if (u8tmp == 0x8f) + if ((utmp & 0x8f) == 0x8f) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto err; } - priv->fe_status = *status; + dev->fe_status = *status; + dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status); + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned int cnr, noise, signal, noise_tot, signal_tot; + + cnr = 0; + /* more iterations for more accurate estimation */ + #define M88DS3103_SNR_ITERATIONS 3 + + switch (c->delivery_system) { + case SYS_DVBS: + itmp = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = regmap_read(dev->regmap, 0xff, &utmp); + if (ret) + goto err; + + itmp += utmp; + } + + /* use of single register limits max value to 15 dB */ + /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ + itmp = DIV_ROUND_CLOSEST(itmp, 8 * M88DS3103_SNR_ITERATIONS); + if (itmp) + cnr = div_u64((u64) 10000 * intlog2(itmp), intlog2(10)); + break; + case SYS_DVBS2: + noise_tot = 0; + signal_tot = 0; + + for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { + ret = regmap_bulk_read(dev->regmap, 0x8c, buf, 3); + if (ret) + goto err; + + noise = buf[1] << 6; /* [13:6] */ + noise |= buf[0] & 0x3f; /* [5:0] */ + noise >>= 2; + signal = buf[2] * buf[2]; + signal >>= 1; + + noise_tot += noise; + signal_tot += signal; + } + + noise = noise_tot / M88DS3103_SNR_ITERATIONS; + signal = signal_tot / M88DS3103_SNR_ITERATIONS; + + /* SNR(X) dB = 10 * log10(X) dB */ + if (signal > noise) { + itmp = signal / noise; + cnr = div_u64((u64) 10000 * intlog10(itmp), (1 << 24)); + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + if (cnr) { + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + unsigned int utmp, post_bit_error, post_bit_count; + + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_write(dev->regmap, 0xf9, 0x04); + if (ret) + goto err; + + ret = regmap_read(dev->regmap, 0xf8, &utmp); + if (ret) + goto err; + + /* measurement ready? */ + if (!(utmp & 0x10)) { + ret = regmap_bulk_read(dev->regmap, 0xf6, buf, 2); + if (ret) + goto err; + + post_bit_error = buf[1] << 8 | buf[0] << 0; + post_bit_count = 0x800000; + dev->post_bit_error += post_bit_error; + dev->post_bit_count += post_bit_count; + dev->dvbv3_ber = post_bit_error; + + /* restart measurement */ + utmp |= 0x10; + ret = regmap_write(dev->regmap, 0xf8, utmp); + if (ret) + goto err; + } + break; + case SYS_DVBS2: + ret = regmap_bulk_read(dev->regmap, 0xd5, buf, 3); + if (ret) + goto err; + + utmp = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + + /* enough data? */ + if (utmp > 4000) { + ret = regmap_bulk_read(dev->regmap, 0xf7, buf, 2); + if (ret) + goto err; + + post_bit_error = buf[1] << 8 | buf[0] << 0; + post_bit_count = 32 * utmp; /* TODO: FEC */ + dev->post_bit_error += post_bit_error; + dev->post_bit_count += post_bit_count; + dev->dvbv3_ber = post_bit_error; + + /* restart measurement */ + ret = regmap_write(dev->regmap, 0xd1, 0x01); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xf9, 0x01); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xf9, 0x00); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xd1, 0x00); + if (ret) + goto err; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } - dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n", - __func__, u8tmp, *status); + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_set_frontend(struct dvb_frontend *fe) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, len; const struct m88ds3103_reg_val *init; @@ -251,29 +310,28 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u32 tuner_frequency, target_mclk; s32 s32tmp; - dev_dbg(&priv->i2c->dev, - "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", - __func__, c->delivery_system, - c->modulation, c->frequency, c->symbol_rate, - c->inversion, c->pilot, c->rolloff); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } /* reset */ - ret = m88ds3103_wr_reg(priv, 0x07, 0x80); + ret = regmap_write(dev->regmap, 0x07, 0x80); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0x07, 0x00); + ret = regmap_write(dev->regmap, 0x07, 0x00); if (ret) goto err; /* Disable demod clock path */ - if (priv->chip_id == M88RS6000_CHIP_ID) { - ret = m88ds3103_wr_reg(priv, 0x06, 0xe0); + if (dev->chip_id == M88RS6000_CHIP_ID) { + ret = regmap_write(dev->regmap, 0x06, 0xe0); if (ret) goto err; } @@ -299,11 +357,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } /* select M88RS6000 demod main mclk and ts mclk from tuner die. */ - if (priv->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID) { if (c->symbol_rate > 45010000) - priv->mclk_khz = 110250; + dev->mclk_khz = 110250; else - priv->mclk_khz = 96000; + dev->mclk_khz = 96000; if (c->delivery_system == SYS_DVBS) target_mclk = 96000; @@ -311,18 +369,18 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) target_mclk = 144000; /* Enable demod clock path */ - ret = m88ds3103_wr_reg(priv, 0x06, 0x00); + ret = regmap_write(dev->regmap, 0x06, 0x00); if (ret) goto err; usleep_range(10000, 20000); } else { /* set M88DS3103 mclk and ts mclk. */ - priv->mclk_khz = 96000; + dev->mclk_khz = 96000; - switch (priv->cfg->ts_mode) { + switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: case M88DS3103_TS_SERIAL_D7: - target_mclk = priv->cfg->ts_clk; + target_mclk = dev->cfg->ts_clk; break; case M88DS3103_TS_PARALLEL: case M88DS3103_TS_CI: @@ -338,8 +396,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", - __func__); + dev_dbg(&client->dev, "invalid ts_mode\n"); ret = -EINVAL; goto err; } @@ -358,25 +415,25 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8tmp2 = 0x00; /* 0b00 */ break; } - ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0); + ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); if (ret) goto err; - ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0); + ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); if (ret) goto err; } - ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); + ret = regmap_write(dev->regmap, 0xb2, 0x01); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0x00, 0x01); + ret = regmap_write(dev->regmap, 0x00, 0x01); if (ret) goto err; switch (c->delivery_system) { case SYS_DVBS: - if (priv->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID) { len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); init = m88rs6000_dvbs_init_reg_vals; } else { @@ -385,7 +442,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } break; case SYS_DVBS2: - if (priv->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID) { len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); init = m88rs6000_dvbs2_init_reg_vals; } else { @@ -394,44 +451,43 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto err; } /* program init table */ - if (c->delivery_system != priv->delivery_system) { - ret = m88ds3103_wr_reg_val_tab(priv, init, len); + if (c->delivery_system != dev->delivery_system) { + ret = m88ds3103_wr_reg_val_tab(dev, init, len); if (ret) goto err; } - if (priv->chip_id == M88RS6000_CHIP_ID) { + if (dev->chip_id == M88RS6000_CHIP_ID) { if ((c->delivery_system == SYS_DVBS2) && ((c->symbol_rate / 1000) <= 5000)) { - ret = m88ds3103_wr_reg(priv, 0xc0, 0x04); + ret = regmap_write(dev->regmap, 0xc0, 0x04); if (ret) goto err; buf[0] = 0x09; buf[1] = 0x22; buf[2] = 0x88; - ret = m88ds3103_wr_regs(priv, 0x8a, buf, 3); + ret = regmap_bulk_write(dev->regmap, 0x8a, buf, 3); if (ret) goto err; } - ret = m88ds3103_wr_reg_mask(priv, 0x9d, 0x08, 0x08); + ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xf1, 0x01); + ret = regmap_write(dev->regmap, 0xf1, 0x01); if (ret) goto err; - ret = m88ds3103_wr_reg_mask(priv, 0x30, 0x80, 0x80); + ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); if (ret) goto err; } - switch (priv->cfg->ts_mode) { + switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: u8tmp1 = 0x00; u8tmp = 0x06; @@ -447,39 +503,39 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8tmp = 0x03; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); + dev_dbg(&client->dev, "invalid ts_mode\n"); ret = -EINVAL; goto err; } - if (priv->cfg->ts_clk_pol) + if (dev->cfg->ts_clk_pol) u8tmp |= 0x40; /* TS mode */ - ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp); + ret = regmap_write(dev->regmap, 0xfd, u8tmp); if (ret) goto err; - switch (priv->cfg->ts_mode) { + switch (dev->cfg->ts_mode) { case M88DS3103_TS_SERIAL: case M88DS3103_TS_SERIAL_D7: - ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20); + ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); if (ret) goto err; u8tmp1 = 0; u8tmp2 = 0; break; default: - if (priv->cfg->ts_clk) { - divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk); + if (dev->cfg->ts_clk) { + divide_ratio = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); u8tmp1 = divide_ratio / 2; u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); } } - dev_dbg(&priv->i2c->dev, - "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n", - __func__, target_mclk, priv->cfg->ts_clk, divide_ratio); + dev_dbg(&client->dev, + "target_mclk=%d ts_clk=%d divide_ratio=%d\n", + target_mclk, dev->cfg->ts_clk, divide_ratio); u8tmp1--; u8tmp2--; @@ -488,17 +544,17 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) /* u8tmp2[5:0] => ea[5:0] */ u8tmp2 &= 0x3f; - ret = m88ds3103_rd_reg(priv, 0xfe, &u8tmp); + ret = regmap_bulk_read(dev->regmap, 0xfe, &u8tmp, 1); if (ret) goto err; u8tmp = ((u8tmp & 0xf0) << 0) | u8tmp1 >> 2; - ret = m88ds3103_wr_reg(priv, 0xfe, u8tmp); + ret = regmap_write(dev->regmap, 0xfe, u8tmp); if (ret) goto err; u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; - ret = m88ds3103_wr_reg(priv, 0xea, u8tmp); + ret = regmap_write(dev->regmap, 0xea, u8tmp); if (ret) goto err; @@ -509,250 +565,254 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) else u8tmp = 0x06; - ret = m88ds3103_wr_reg(priv, 0xc3, 0x08); + ret = regmap_write(dev->regmap, 0xc3, 0x08); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xc8, u8tmp); + ret = regmap_write(dev->regmap, 0xc8, u8tmp); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xc4, 0x08); + ret = regmap_write(dev->regmap, 0xc4, 0x08); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xc7, 0x00); + ret = regmap_write(dev->regmap, 0xc7, 0x00); if (ret) goto err; - u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, priv->mclk_khz / 2); + u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, dev->mclk_khz / 2); buf[0] = (u16tmp >> 0) & 0xff; buf[1] = (u16tmp >> 8) & 0xff; - ret = m88ds3103_wr_regs(priv, 0x61, buf, 2); + ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); if (ret) goto err; - ret = m88ds3103_wr_reg_mask(priv, 0x4d, priv->cfg->spec_inv << 1, 0x02); + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); if (ret) goto err; - ret = m88ds3103_wr_reg_mask(priv, 0x30, priv->cfg->agc_inv << 4, 0x10); + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0x33, priv->cfg->agc); + ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc); if (ret) goto err; - dev_dbg(&priv->i2c->dev, "%s: carrier offset=%d\n", __func__, - (tuner_frequency - c->frequency)); + dev_dbg(&client->dev, "carrier offset=%d\n", + (tuner_frequency - c->frequency)); s32tmp = 0x10000 * (tuner_frequency - c->frequency); - s32tmp = DIV_ROUND_CLOSEST(s32tmp, priv->mclk_khz); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz); if (s32tmp < 0) s32tmp += 0x10000; buf[0] = (s32tmp >> 0) & 0xff; buf[1] = (s32tmp >> 8) & 0xff; - ret = m88ds3103_wr_regs(priv, 0x5e, buf, 2); + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0x00, 0x00); + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xb2, 0x00); + ret = regmap_write(dev->regmap, 0xb2, 0x00); if (ret) goto err; - priv->delivery_system = c->delivery_system; + dev->delivery_system = c->delivery_system; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_init(struct dvb_frontend *fe) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, len, remaining; + unsigned int utmp; const struct firmware *fw = NULL; u8 *fw_file; - u8 u8tmp; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); /* set cold state by default */ - priv->warm = false; + dev->warm = false; /* wake up device from sleep */ - ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); if (ret) goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01); + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); if (ret) goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10); + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); if (ret) goto err; /* firmware status */ - ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp); + ret = regmap_read(dev->regmap, 0xb9, &utmp); if (ret) goto err; - dev_dbg(&priv->i2c->dev, "%s: firmware=%02x\n", __func__, u8tmp); + dev_dbg(&client->dev, "firmware=%02x\n", utmp); - if (u8tmp) + if (utmp) goto skip_fw_download; /* global reset, global diseqc reset, golbal fec reset */ - ret = m88ds3103_wr_reg(priv, 0x07, 0xe0); + ret = regmap_write(dev->regmap, 0x07, 0xe0); if (ret) goto err; - - ret = m88ds3103_wr_reg(priv, 0x07, 0x00); + ret = regmap_write(dev->regmap, 0x07, 0x00); if (ret) goto err; /* cold state - try to download firmware */ - dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n", - KBUILD_MODNAME, m88ds3103_ops.info.name); + dev_info(&client->dev, "found a '%s' in cold state\n", + m88ds3103_ops.info.name); - if (priv->chip_id == M88RS6000_CHIP_ID) + if (dev->chip_id == M88RS6000_CHIP_ID) fw_file = M88RS6000_FIRMWARE; else fw_file = M88DS3103_FIRMWARE; /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + ret = request_firmware(&fw, fw_file, &client->dev); if (ret) { - dev_err(&priv->i2c->dev, "%s: firmware file '%s' not found\n", - KBUILD_MODNAME, fw_file); + dev_err(&client->dev, "firmare file '%s' not found\n", fw_file); goto err; } - dev_info(&priv->i2c->dev, "%s: downloading firmware from file '%s'\n", - KBUILD_MODNAME, fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_file); - ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); + ret = regmap_write(dev->regmap, 0xb2, 0x01); if (ret) goto error_fw_release; for (remaining = fw->size; remaining > 0; - remaining -= (priv->cfg->i2c_wr_max - 1)) { + remaining -= (dev->cfg->i2c_wr_max - 1)) { len = remaining; - if (len > (priv->cfg->i2c_wr_max - 1)) - len = (priv->cfg->i2c_wr_max - 1); + if (len > (dev->cfg->i2c_wr_max - 1)) + len = (dev->cfg->i2c_wr_max - 1); - ret = m88ds3103_wr_regs(priv, 0xb0, + ret = regmap_bulk_write(dev->regmap, 0xb0, &fw->data[fw->size - remaining], len); if (ret) { - dev_err(&priv->i2c->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); + dev_err(&client->dev, "firmware download failed=%d\n", + ret); goto error_fw_release; } } - ret = m88ds3103_wr_reg(priv, 0xb2, 0x00); + ret = regmap_write(dev->regmap, 0xb2, 0x00); if (ret) goto error_fw_release; release_firmware(fw); fw = NULL; - ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp); + ret = regmap_read(dev->regmap, 0xb9, &utmp); if (ret) goto err; - if (!u8tmp) { - dev_info(&priv->i2c->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); + if (!utmp) { + dev_info(&client->dev, "firmware did not run\n"); ret = -EFAULT; goto err; } - dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", - KBUILD_MODNAME, m88ds3103_ops.info.name); - dev_info(&priv->i2c->dev, "%s: firmware version %X.%X\n", - KBUILD_MODNAME, (u8tmp >> 4) & 0xf, (u8tmp >> 0 & 0xf)); + dev_info(&client->dev, "found a '%s' in warm state\n", + m88ds3103_ops.info.name); + dev_info(&client->dev, "firmware version: %X.%X\n", + (utmp >> 4) & 0xf, (utmp >> 0 & 0xf)); skip_fw_download: /* warm state */ - priv->warm = true; + dev->warm = true; - return 0; + /* init stats here in order signal app which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; error_fw_release: release_firmware(fw); err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_sleep(struct dvb_frontend *fe) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; - u8 u8tmp; + unsigned int utmp; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); - priv->delivery_system = SYS_UNDEFINED; + dev->fe_status = 0; + dev->delivery_system = SYS_UNDEFINED; /* TS Hi-Z */ - if (priv->chip_id == M88RS6000_CHIP_ID) - u8tmp = 0x29; + if (dev->chip_id == M88RS6000_CHIP_ID) + utmp = 0x29; else - u8tmp = 0x27; - ret = m88ds3103_wr_reg_mask(priv, u8tmp, 0x00, 0x01); + utmp = 0x27; + ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00); if (ret) goto err; /* sleep */ - ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); if (ret) goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01); + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); if (ret) goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10); + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); if (ret) goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_get_frontend(struct dvb_frontend *fe) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[3]; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - ret = -EAGAIN; + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + ret = 0; goto err; } switch (c->delivery_system) { case SYS_DVBS: - ret = m88ds3103_rd_reg(priv, 0xe0, &buf[0]); + ret = regmap_bulk_read(dev->regmap, 0xe0, &buf[0], 1); if (ret) goto err; - ret = m88ds3103_rd_reg(priv, 0xe6, &buf[1]); + ret = regmap_bulk_read(dev->regmap, 0xe6, &buf[1], 1); if (ret) goto err; @@ -782,23 +842,22 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe) c->fec_inner = FEC_1_2; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n", - __func__); + dev_dbg(&client->dev, "invalid fec_inner\n"); } c->modulation = QPSK; break; case SYS_DVBS2: - ret = m88ds3103_rd_reg(priv, 0x7e, &buf[0]); + ret = regmap_bulk_read(dev->regmap, 0x7e, &buf[0], 1); if (ret) goto err; - ret = m88ds3103_rd_reg(priv, 0x89, &buf[1]); + ret = regmap_bulk_read(dev->regmap, 0x89, &buf[1], 1); if (ret) goto err; - ret = m88ds3103_rd_reg(priv, 0xf2, &buf[2]); + ret = regmap_bulk_read(dev->regmap, 0xf2, &buf[2], 1); if (ret) goto err; @@ -831,8 +890,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe) c->fec_inner = FEC_9_10; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n", - __func__); + dev_dbg(&client->dev, "invalid fec_inner\n"); } switch ((buf[0] >> 5) & 0x01) { @@ -858,8 +916,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe) c->modulation = APSK_32; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid modulation\n", - __func__); + dev_dbg(&client->dev, "invalid modulation\n"); } switch ((buf[1] >> 7) & 0x01) { @@ -882,201 +939,60 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe) c->rolloff = ROLLOFF_20; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", - __func__); + dev_dbg(&client->dev, "invalid rolloff\n"); } break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto err; } - ret = m88ds3103_rd_regs(priv, 0x6d, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x6d, buf, 2); if (ret) goto err; c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) * - priv->mclk_khz * 1000 / 0x10000; + dev->mclk_khz * 1000 / 0x10000; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct m88ds3103_priv *priv = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i, tmp; - u8 buf[3]; - u16 noise, signal; - u32 noise_tot, signal_tot; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - /* reports SNR in resolution of 0.1 dB */ - - /* more iterations for more accurate estimation */ - #define M88DS3103_SNR_ITERATIONS 3 - - switch (c->delivery_system) { - case SYS_DVBS: - tmp = 0; - - for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { - ret = m88ds3103_rd_reg(priv, 0xff, &buf[0]); - if (ret) - goto err; - - tmp += buf[0]; - } - - /* use of one register limits max value to 15 dB */ - /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ - tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS); - if (tmp) - *snr = div_u64((u64) 100 * intlog2(tmp), intlog2(10)); - else - *snr = 0; - break; - case SYS_DVBS2: - noise_tot = 0; - signal_tot = 0; - - for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { - ret = m88ds3103_rd_regs(priv, 0x8c, buf, 3); - if (ret) - goto err; - noise = buf[1] << 6; /* [13:6] */ - noise |= buf[0] & 0x3f; /* [5:0] */ - noise >>= 2; - signal = buf[2] * buf[2]; - signal >>= 1; - - noise_tot += noise; - signal_tot += signal; - } - - noise = noise_tot / M88DS3103_SNR_ITERATIONS; - signal = signal_tot / M88DS3103_SNR_ITERATIONS; - - /* SNR(X) dB = 10 * log10(X) dB */ - if (signal > noise) { - tmp = signal / noise; - *snr = div_u64((u64) 100 * intlog10(tmp), (1 << 24)); - } else { - *snr = 0; - } - break; - default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); - ret = -EINVAL; - goto err; - } + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; return 0; -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; } static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber) { - struct m88ds3103_priv *priv = fe->demodulator_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - unsigned int utmp; - u8 buf[3], u8tmp; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + struct m88ds3103_dev *dev = fe->demodulator_priv; - switch (c->delivery_system) { - case SYS_DVBS: - ret = m88ds3103_wr_reg(priv, 0xf9, 0x04); - if (ret) - goto err; - - ret = m88ds3103_rd_reg(priv, 0xf8, &u8tmp); - if (ret) - goto err; - - if (!(u8tmp & 0x10)) { - u8tmp |= 0x10; - - ret = m88ds3103_rd_regs(priv, 0xf6, buf, 2); - if (ret) - goto err; - - priv->ber = (buf[1] << 8) | (buf[0] << 0); - - /* restart counters */ - ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp); - if (ret) - goto err; - } - break; - case SYS_DVBS2: - ret = m88ds3103_rd_regs(priv, 0xd5, buf, 3); - if (ret) - goto err; - - utmp = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); - - if (utmp > 3000) { - ret = m88ds3103_rd_regs(priv, 0xf7, buf, 2); - if (ret) - goto err; - - priv->ber = (buf[1] << 8) | (buf[0] << 0); - - /* restart counters */ - ret = m88ds3103_wr_reg(priv, 0xd1, 0x01); - if (ret) - goto err; - - ret = m88ds3103_wr_reg(priv, 0xf9, 0x01); - if (ret) - goto err; - - ret = m88ds3103_wr_reg(priv, 0xf9, 0x00); - if (ret) - goto err; - - ret = m88ds3103_wr_reg(priv, 0xd1, 0x00); - if (ret) - goto err; - } - break; - default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); - ret = -EINVAL; - goto err; - } - - *ber = priv->ber; + *ber = dev->dvbv3_ber; return 0; -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; } static int m88ds3103_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t fe_sec_tone_mode) + enum fe_sec_tone_mode fe_sec_tone_mode) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; - u8 u8tmp, tone, reg_a1_mask; + unsigned int utmp, tone, reg_a1_mask; - dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__, - fe_sec_tone_mode); + dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode); - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } @@ -1091,40 +1007,39 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, reg_a1_mask = 0x00; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n"); ret = -EINVAL; goto err; } - u8tmp = tone << 7 | priv->cfg->envelope_mode << 5; - ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + utmp = tone << 7 | dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; - u8tmp = 1 << 2; - ret = m88ds3103_wr_reg_mask(priv, 0xa1, u8tmp, reg_a1_mask); + utmp = 1 << 2; + ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp); if (ret) goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t fe_sec_voltage) + enum fe_sec_voltage fe_sec_voltage) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; - u8 u8tmp; + unsigned int utmp; bool voltage_sel, voltage_dis; - dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, - fe_sec_voltage); + dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } @@ -1143,38 +1058,39 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe, voltage_dis = true; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_voltage\n"); ret = -EINVAL; goto err; } /* output pin polarity */ - voltage_sel ^= priv->cfg->lnb_hv_pol; - voltage_dis ^= priv->cfg->lnb_en_pol; + voltage_sel ^= dev->cfg->lnb_hv_pol; + voltage_dis ^= dev->cfg->lnb_en_pol; - u8tmp = voltage_dis << 1 | voltage_sel << 0; - ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0x03); + utmp = voltage_dis << 1 | voltage_sel << 0; + ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp); if (ret) goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *diseqc_cmd) { - struct m88ds3103_priv *priv = fe->demodulator_priv; - int ret, i; - u8 u8tmp; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp; + unsigned long timeout; - dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__, - diseqc_cmd->msg_len, diseqc_cmd->msg); + dev_dbg(&client->dev, "msg=%*ph\n", + diseqc_cmd->msg_len, diseqc_cmd->msg); - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } @@ -1184,75 +1100,80 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, goto err; } - u8tmp = priv->cfg->envelope_mode << 5; - ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + utmp = dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; - ret = m88ds3103_wr_regs(priv, 0xa3, diseqc_cmd->msg, + ret = regmap_bulk_write(dev->regmap, 0xa3, diseqc_cmd->msg, diseqc_cmd->msg_len); if (ret) goto err; - ret = m88ds3103_wr_reg(priv, 0xa1, + ret = regmap_write(dev->regmap, 0xa1, (diseqc_cmd->msg_len - 1) << 3 | 0x07); if (ret) goto err; - /* DiSEqC message typical period is 54 ms */ - usleep_range(40000, 60000); - /* wait DiSEqC TX ready */ - for (i = 20, u8tmp = 1; i && u8tmp; i--) { - usleep_range(5000, 10000); + #define SEND_MASTER_CMD_TIMEOUT 120 + timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT); - ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40); + /* DiSEqC message typical period is 54 ms */ + usleep_range(50000, 54000); + + for (utmp = 1; !time_after(jiffies, timeout) && utmp;) { + ret = regmap_read(dev->regmap, 0xa1, &utmp); if (ret) goto err; + utmp = (utmp >> 6) & 0x1; } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); - - if (i == 0) { - dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__); + if (utmp == 0) { + dev_dbg(&client->dev, "diseqc tx took %u ms\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - SEND_MASTER_CMD_TIMEOUT)); + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); - ret = m88ds3103_wr_reg_mask(priv, 0xa1, 0x40, 0xc0); + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); if (ret) goto err; } - ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0); + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); if (ret) goto err; - if (i == 0) { + if (utmp == 1) { ret = -ETIMEDOUT; goto err; } return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t fe_sec_mini_cmd) + enum fe_sec_mini_cmd fe_sec_mini_cmd) { - struct m88ds3103_priv *priv = fe->demodulator_priv; - int ret, i; - u8 u8tmp, burst; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + unsigned int utmp, burst; + unsigned long timeout; - dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, - fe_sec_mini_cmd); + dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd); - if (!priv->warm) { + if (!dev->warm) { ret = -EAGAIN; goto err; } - u8tmp = priv->cfg->envelope_mode << 5; - ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0); + utmp = dev->cfg->envelope_mode << 5; + ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); if (ret) goto err; @@ -1264,43 +1185,53 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, burst = 0x01; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n"); ret = -EINVAL; goto err; } - ret = m88ds3103_wr_reg(priv, 0xa1, burst); + ret = regmap_write(dev->regmap, 0xa1, burst); if (ret) goto err; - /* DiSEqC ToneBurst period is 12.5 ms */ - usleep_range(11000, 20000); - /* wait DiSEqC TX ready */ - for (i = 5, u8tmp = 1; i && u8tmp; i--) { - usleep_range(800, 2000); + #define SEND_BURST_TIMEOUT 40 + timeout = jiffies + msecs_to_jiffies(SEND_BURST_TIMEOUT); + + /* DiSEqC ToneBurst period is 12.5 ms */ + usleep_range(8500, 12500); - ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40); + for (utmp = 1; !time_after(jiffies, timeout) && utmp;) { + ret = regmap_read(dev->regmap, 0xa1, &utmp); if (ret) goto err; + utmp = (utmp >> 6) & 0x1; } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + if (utmp == 0) { + dev_dbg(&client->dev, "diseqc tx took %u ms\n", + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - SEND_BURST_TIMEOUT)); + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); + + ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); + if (ret) + goto err; + } - ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0); + ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); if (ret) goto err; - if (i == 0) { - dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__); + if (utmp == 1) { ret = -ETIMEDOUT; goto err; } return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -1314,193 +1245,289 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, static void m88ds3103_release(struct dvb_frontend *fe) { - struct m88ds3103_priv *priv = fe->demodulator_priv; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; - i2c_del_mux_adapter(priv->i2c_adapter); - kfree(priv); + i2c_unregister_device(client); } static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) { - struct m88ds3103_priv *priv = mux_priv; + struct m88ds3103_dev *dev = mux_priv; + struct i2c_client *client = dev->client; int ret; - struct i2c_msg gate_open_msg[1] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = 2, - .buf = "\x03\x11", - } + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = "\x03\x11", }; - mutex_lock(&priv->i2c_mutex); - - /* open tuner I2C repeater for 1 xfer, closes automatically */ - ret = __i2c_transfer(priv->i2c, gate_open_msg, 1); + /* Open tuner I2C repeater for 1 xfer, closes automatically */ + ret = __i2c_transfer(client->adapter, &msg, 1); if (ret != 1) { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n", - KBUILD_MODNAME, ret); + dev_warn(&client->dev, "i2c wr failed=%d\n", ret); if (ret >= 0) ret = -EREMOTEIO; - return ret; } return 0; } -static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv, - u32 chan) +/* + * XXX: That is wrapper to m88ds3103_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 *m88ds3103_attach(const struct m88ds3103_config *cfg, + struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter) { - struct m88ds3103_priv *priv = mux_priv; + struct i2c_client *client; + struct i2c_board_info board_info; + struct m88ds3103_platform_data pdata; + + pdata.clk = cfg->clock; + pdata.i2c_wr_max = cfg->i2c_wr_max; + pdata.ts_mode = cfg->ts_mode; + pdata.ts_clk = cfg->ts_clk; + pdata.ts_clk_pol = cfg->ts_clk_pol; + pdata.spec_inv = cfg->spec_inv; + pdata.agc = cfg->agc; + pdata.agc_inv = cfg->agc_inv; + pdata.clk_out = cfg->clock_out; + pdata.envelope_mode = cfg->envelope_mode; + pdata.lnb_hv_pol = cfg->lnb_hv_pol; + pdata.lnb_en_pol = cfg->lnb_en_pol; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); + board_info.addr = cfg->i2c_addr; + board_info.platform_data = &pdata; + client = i2c_new_device(i2c, &board_info); + if (!client || !client->dev.driver) + return NULL; + + *tuner_i2c_adapter = pdata.get_i2c_adapter(client); + return pdata.get_dvb_frontend(client); +} +EXPORT_SYMBOL(m88ds3103_attach); - mutex_unlock(&priv->i2c_mutex); +static struct dvb_frontend_ops m88ds3103_ops = { + .delsys = {SYS_DVBS, SYS_DVBS2}, + .info = { + .name = "Montage Technology M88DS3103", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_RECOVER | + FE_CAN_2G_MODULATION + }, - return 0; + .release = m88ds3103_release, + + .get_tune_settings = m88ds3103_get_tune_settings, + + .init = m88ds3103_init, + .sleep = m88ds3103_sleep, + + .set_frontend = m88ds3103_set_frontend, + .get_frontend = m88ds3103_get_frontend, + + .read_status = m88ds3103_read_status, + .read_snr = m88ds3103_read_snr, + .read_ber = m88ds3103_read_ber, + + .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, + .diseqc_send_burst = m88ds3103_diseqc_send_burst, + + .set_tone = m88ds3103_set_tone, + .set_voltage = m88ds3103_set_voltage, +}; + +static struct dvb_frontend *m88ds3103_get_dvb_frontend(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; } -struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, - struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter) +static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return dev->i2c_adapter; +} + +static int m88ds3103_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct m88ds3103_dev *dev; + struct m88ds3103_platform_data *pdata = client->dev.platform_data; int ret; - struct m88ds3103_priv *priv; - u8 chip_id, u8tmp; + unsigned int utmp; - /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; - dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); goto err; } - priv->cfg = cfg; - priv->i2c = i2c; - mutex_init(&priv->i2c_mutex); + dev->client = client; + dev->config.clock = pdata->clk; + dev->config.i2c_wr_max = pdata->i2c_wr_max; + dev->config.ts_mode = pdata->ts_mode; + dev->config.ts_clk = pdata->ts_clk; + dev->config.ts_clk_pol = pdata->ts_clk_pol; + dev->config.spec_inv = pdata->spec_inv; + dev->config.agc_inv = pdata->agc_inv; + dev->config.clock_out = pdata->clk_out; + dev->config.envelope_mode = pdata->envelope_mode; + dev->config.agc = pdata->agc; + dev->config.lnb_hv_pol = pdata->lnb_hv_pol; + dev->config.lnb_en_pol = pdata->lnb_en_pol; + dev->cfg = &dev->config; + /* create regmap */ + dev->regmap_config.reg_bits = 8, + dev->regmap_config.val_bits = 8, + dev->regmap_config.lock_arg = dev, + dev->regmap = devm_regmap_init_i2c(client, &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */ - ret = m88ds3103_rd_reg(priv, 0x00, &chip_id); + ret = regmap_read(dev->regmap, 0x00, &utmp); if (ret) - goto err; + goto err_kfree; - chip_id >>= 1; - dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + dev->chip_id = utmp >> 1; + dev_dbg(&client->dev, "chip_id=%02x\n", dev->chip_id); - switch (chip_id) { + switch (dev->chip_id) { case M88RS6000_CHIP_ID: case M88DS3103_CHIP_ID: break; default: - goto err; + goto err_kfree; } - priv->chip_id = chip_id; - switch (priv->cfg->clock_out) { + switch (dev->cfg->clock_out) { case M88DS3103_CLOCK_OUT_DISABLED: - u8tmp = 0x80; + utmp = 0x80; break; case M88DS3103_CLOCK_OUT_ENABLED: - u8tmp = 0x00; + utmp = 0x00; break; case M88DS3103_CLOCK_OUT_ENABLED_DIV2: - u8tmp = 0x10; + utmp = 0x10; break; default: - goto err; + ret = -EINVAL; + goto err_kfree; } /* 0x29 register is defined differently for m88rs6000. */ /* set internal tuner address to 0x21 */ - if (chip_id == M88RS6000_CHIP_ID) - u8tmp = 0x00; + if (dev->chip_id == M88RS6000_CHIP_ID) + utmp = 0x00; - ret = m88ds3103_wr_reg(priv, 0x29, u8tmp); + ret = regmap_write(dev->regmap, 0x29, utmp); if (ret) - goto err; + goto err_kfree; /* sleep */ - ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01); + ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); if (ret) - goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01); + goto err_kfree; + ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); if (ret) - goto err; - - ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10); + goto err_kfree; + ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); if (ret) - goto err; + goto err_kfree; /* create mux i2c adapter for tuner */ - priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0, - m88ds3103_select, m88ds3103_deselect); - if (priv->i2c_adapter == NULL) - goto err; - - *tuner_i2c_adapter = priv->i2c_adapter; + dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev, + dev, 0, 0, 0, m88ds3103_select, + NULL); + if (dev->i2c_adapter == NULL) { + ret = -ENOMEM; + goto err_kfree; + } /* create dvb_frontend */ - memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); - if (priv->chip_id == M88RS6000_CHIP_ID) - strncpy(priv->fe.ops.info.name, - "Montage M88RS6000", sizeof(priv->fe.ops.info.name)); - priv->fe.demodulator_priv = priv; - - return &priv->fe; + memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); + if (dev->chip_id == M88RS6000_CHIP_ID) + strncpy(dev->fe.ops.info.name, "Montage Technology M88RS6000", + sizeof(dev->fe.ops.info.name)); + if (!pdata->attach_in_use) + dev->fe.ops.release = NULL; + dev->fe.demodulator_priv = dev; + i2c_set_clientdata(client, dev); + + /* setup callbacks */ + pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend; + pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter; + return 0; +err_kfree: + kfree(dev); err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(m88ds3103_attach); -static struct dvb_frontend_ops m88ds3103_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2 }, - .info = { - .name = "Montage M88DS3103", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_tolerance = 5000, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_4_5 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_6_7 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_8_9 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_RECOVER | - FE_CAN_2G_MODULATION - }, - - .release = m88ds3103_release, +static int m88ds3103_remove(struct i2c_client *client) +{ + struct m88ds3103_dev *dev = i2c_get_clientdata(client); - .get_tune_settings = m88ds3103_get_tune_settings, + dev_dbg(&client->dev, "\n"); - .init = m88ds3103_init, - .sleep = m88ds3103_sleep, + i2c_del_mux_adapter(dev->i2c_adapter); - .set_frontend = m88ds3103_set_frontend, - .get_frontend = m88ds3103_get_frontend, - - .read_status = m88ds3103_read_status, - .read_snr = m88ds3103_read_snr, - .read_ber = m88ds3103_read_ber, + kfree(dev); + return 0; +} - .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, - .diseqc_send_burst = m88ds3103_diseqc_send_burst, +static const struct i2c_device_id m88ds3103_id_table[] = { + {"m88ds3103", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); - .set_tone = m88ds3103_set_tone, - .set_voltage = m88ds3103_set_voltage, +static struct i2c_driver m88ds3103_driver = { + .driver = { + .name = "m88ds3103", + .suppress_bind_attrs = true, + }, + .probe = m88ds3103_probe, + .remove = m88ds3103_remove, + .id_table = m88ds3103_id_table, }; +module_i2c_driver(m88ds3103_driver); + MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver"); +MODULE_DESCRIPTION("Montage Technology M88DS3103 DVB-S/S2 demodulator driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(M88DS3103_FIRMWARE); MODULE_FIRMWARE(M88RS6000_FIRMWARE); diff --git a/kernel/drivers/media/dvb-frontends/m88ds3103.h b/kernel/drivers/media/dvb-frontends/m88ds3103.h index 9b3b4962d..04b355a00 100644 --- a/kernel/drivers/media/dvb-frontends/m88ds3103.h +++ b/kernel/drivers/media/dvb-frontends/m88ds3103.h @@ -1,5 +1,5 @@ /* - * Montage M88DS3103 demodulator driver + * Montage Technology M88DS3103/M88RS6000 demodulator driver * * Copyright (C) 2013 Antti Palosaari * @@ -19,6 +19,63 @@ #include +/* + * I2C address + * 0x68, + */ + +/** + * struct m88ds3103_platform_data - Platform data for the m88ds3103 driver + * @clk: Clock frequency. + * @i2c_wr_max: Max bytes I2C adapter can write at once. + * @ts_mode: TS mode. + * @ts_clk: TS clock (KHz). + * @ts_clk_pol: TS clk polarity. 1-active at falling edge; 0-active at rising + * edge. + * @spec_inv: Input spectrum inversion. + * @agc: AGC configuration. + * @agc_inv: AGC polarity. + * @clk_out: Clock output. + * @envelope_mode: DiSEqC envelope mode. + * @lnb_hv_pol: LNB H/V pin polarity. 0: pin high set to VOLTAGE_18, pin low to + * set VOLTAGE_13. 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18. + * @lnb_en_pol: LNB enable pin polarity. 0: pin high to disable, pin low to + * enable. 1: pin high to enable, pin low to disable. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + */ + +struct m88ds3103_platform_data { + u32 clk; + u16 i2c_wr_max; +#define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */ +#define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */ +#define M88DS3103_TS_PARALLEL 2 /* TS Parallel mode */ +#define M88DS3103_TS_CI 3 /* TS CI Mode */ + u8 ts_mode:2; + u32 ts_clk; + u8 ts_clk_pol:1; + u8 spec_inv:1; + u8 agc; + u8 agc_inv:1; +#define M88DS3103_CLOCK_OUT_DISABLED 0 +#define M88DS3103_CLOCK_OUT_ENABLED 1 +#define M88DS3103_CLOCK_OUT_ENABLED_DIV2 2 + u8 clk_out:2; + u8 envelope_mode:1; + u8 lnb_hv_pol:1; + u8 lnb_en_pol:1; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + +/* private: For legacy media attach wrapper. Do not set value. */ + u8 attach_in_use:1; +}; + +/* + * Do not add new m88ds3103_attach() users! Use I2C bindings instead. + */ struct m88ds3103_config { /* * I2C address @@ -113,18 +170,13 @@ struct m88ds3103_config { u8 lnb_en_pol:1; }; -/* - * Driver implements own I2C-adapter for tuner I2C access. That's since chip - * has I2C-gate control which closes gate automatically after I2C transfer. - * Using own I2C adapter we can workaround that. - */ - #if defined(CONFIG_DVB_M88DS3103) || \ (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE)) extern struct dvb_frontend *m88ds3103_attach( const struct m88ds3103_config *config, struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c); +extern int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm); #else static inline struct dvb_frontend *m88ds3103_attach( const struct m88ds3103_config *config, @@ -134,6 +186,7 @@ static inline struct dvb_frontend *m88ds3103_attach( pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } +#define m88ds3103_get_agc_pwm NULL #endif #endif diff --git a/kernel/drivers/media/dvb-frontends/m88ds3103_priv.h b/kernel/drivers/media/dvb-frontends/m88ds3103_priv.h index a2c095811..eee8c22c5 100644 --- a/kernel/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/kernel/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -1,5 +1,5 @@ /* - * Montage M88DS3103 demodulator driver + * Montage Technology M88DS3103/M88RS6000 demodulator driver * * Copyright (C) 2013 Antti Palosaari * @@ -22,6 +22,7 @@ #include "dvb_math.h" #include #include +#include #include #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" @@ -30,21 +31,24 @@ #define M88RS6000_CHIP_ID 0x74 #define M88DS3103_CHIP_ID 0x70 -struct m88ds3103_priv { - struct i2c_adapter *i2c; - /* mutex needed due to own tuner I2C adapter */ - struct mutex i2c_mutex; +struct m88ds3103_dev { + struct i2c_client *client; + struct regmap_config regmap_config; + struct regmap *regmap; + struct m88ds3103_config config; const struct m88ds3103_config *cfg; struct dvb_frontend fe; - fe_delivery_system_t delivery_system; - fe_status_t fe_status; - u32 ber; + enum fe_delivery_system delivery_system; + enum fe_status fe_status; + u32 dvbv3_ber; /* for old DVBv3 API read_ber */ bool warm; /* FW running */ struct i2c_adapter *i2c_adapter; /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ u32 mclk_khz; + u64 post_bit_error; + u64 post_bit_count; }; struct m88ds3103_reg_val { diff --git a/kernel/drivers/media/dvb-frontends/m88rs2000.c b/kernel/drivers/media/dvb-frontends/m88rs2000.c index d63bc9c13..9b6f464c4 100644 --- a/kernel/drivers/media/dvb-frontends/m88rs2000.c +++ b/kernel/drivers/media/dvb-frontends/m88rs2000.c @@ -41,7 +41,7 @@ struct m88rs2000_state { u8 no_lock_count; u32 tuner_frequency; u32 symbol_rate; - fe_code_rate_t fec_inner; + enum fe_code_rate fec_inner; u8 tuner_level; int errmode; }; @@ -247,7 +247,7 @@ static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, } static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; @@ -264,7 +264,8 @@ static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, return 0; } -static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int m88rs2000_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; @@ -412,7 +413,8 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, return 0; } -static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +static int m88rs2000_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage volt) { struct m88rs2000_state *state = fe->demodulator_priv; u8 data; @@ -462,7 +464,8 @@ static int m88rs2000_sleep(struct dvb_frontend *fe) return ret; } -static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int m88rs2000_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg = m88rs2000_readreg(state, 0x8c); @@ -539,7 +542,7 @@ static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) } static int m88rs2000_set_fec(struct m88rs2000_state *state, - fe_code_rate_t fec) + enum fe_code_rate fec) { u8 fec_set, reg; int ret; @@ -574,7 +577,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, return ret; } -static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) +static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state) { u8 reg; m88rs2000_writereg(state, 0x9a, 0x30); @@ -606,7 +609,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - fe_status_t status; + enum fe_status status; int i, ret = 0; u32 tuner_freq; s16 offset = 0; diff --git a/kernel/drivers/media/dvb-frontends/mb86a16.c b/kernel/drivers/media/dvb-frontends/mb86a16.c index 3ddea4471..79bc671e8 100644 --- a/kernel/drivers/media/dvb-frontends/mb86a16.c +++ b/kernel/drivers/media/dvb-frontends/mb86a16.c @@ -593,7 +593,7 @@ err: return -EREMOTEIO; } -static int mb86a16_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int mb86a16_read_status(struct dvb_frontend *fe, enum fe_status *status) { u8 stat, stat2; struct mb86a16_state *state = fe->demodulator_priv; @@ -1562,7 +1562,8 @@ err: return -EREMOTEIO; } -static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct mb86a16_state *state = fe->demodulator_priv; @@ -1590,7 +1591,7 @@ err: return -EREMOTEIO; } -static int mb86a16_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int mb86a16_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct mb86a16_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/mb86a16.h b/kernel/drivers/media/dvb-frontends/mb86a16.h index e486dc0d8..dbd5f43fa 100644 --- a/kernel/drivers/media/dvb-frontends/mb86a16.h +++ b/kernel/drivers/media/dvb-frontends/mb86a16.h @@ -28,7 +28,8 @@ struct mb86a16_config { u8 demod_address; - int (*set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); }; diff --git a/kernel/drivers/media/dvb-frontends/mb86a20s.c b/kernel/drivers/media/dvb-frontends/mb86a20s.c index 8f54c39ca..cfc005ee1 100644 --- a/kernel/drivers/media/dvb-frontends/mb86a20s.c +++ b/kernel/drivers/media/dvb-frontends/mb86a20s.c @@ -294,7 +294,7 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, * The functions below assume that gateway lock has already obtained */ -static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct mb86a20s_state *state = fe->demodulator_priv; int val; @@ -1951,7 +1951,7 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) } static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { struct mb86a20s_state *state = fe->demodulator_priv; int rc, status_nr; @@ -2042,7 +2042,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { struct mb86a20s_state *state = fe->demodulator_priv; int rc = 0; diff --git a/kernel/drivers/media/dvb-frontends/mb86a20s.h b/kernel/drivers/media/dvb-frontends/mb86a20s.h index f749c8ac5..a113282d6 100644 --- a/kernel/drivers/media/dvb-frontends/mb86a20s.h +++ b/kernel/drivers/media/dvb-frontends/mb86a20s.h @@ -45,7 +45,7 @@ static inline struct dvb_frontend *mb86a20s_attach( printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static struct i2c_adapter * +static inline struct i2c_adapter * mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *fe) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/kernel/drivers/media/dvb-frontends/mt312.c b/kernel/drivers/media/dvb-frontends/mt312.c index 2163490c1..c36e6764e 100644 --- a/kernel/drivers/media/dvb-frontends/mt312.c +++ b/kernel/drivers/media/dvb-frontends/mt312.c @@ -156,7 +156,7 @@ static int mt312_reset(struct mt312_state *state, const u8 full) } static int mt312_get_inversion(struct mt312_state *state, - fe_spectral_inversion_t *i) + enum fe_spectral_inversion *i) { int ret; u8 vit_mode; @@ -225,9 +225,9 @@ static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) return 0; } -static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr) +static int mt312_get_code_rate(struct mt312_state *state, enum fe_code_rate *cr) { - const fe_code_rate_t fec_tab[8] = + const enum fe_code_rate fec_tab[8] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, FEC_AUTO, FEC_AUTO }; @@ -380,7 +380,8 @@ static int mt312_send_master_cmd(struct dvb_frontend *fe, return 0; } -static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c) +static int mt312_send_burst(struct dvb_frontend *fe, + const enum fe_sec_mini_cmd c) { struct mt312_state *state = fe->demodulator_priv; const u8 mini_tab[2] = { 0x02, 0x03 }; @@ -403,7 +404,8 @@ static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c) return 0; } -static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t) +static int mt312_set_tone(struct dvb_frontend *fe, + const enum fe_sec_tone_mode t) { struct mt312_state *state = fe->demodulator_priv; const u8 tone_tab[2] = { 0x01, 0x00 }; @@ -426,7 +428,8 @@ static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t) return 0; } -static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v) +static int mt312_set_voltage(struct dvb_frontend *fe, + const enum fe_sec_voltage v) { struct mt312_state *state = fe->demodulator_priv; const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; @@ -442,7 +445,7 @@ static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v) return mt312_writereg(state, DISEQC_MODE, val); } -static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s) +static int mt312_read_status(struct dvb_frontend *fe, enum fe_status *s) { struct mt312_state *state = fe->demodulator_priv; int ret; diff --git a/kernel/drivers/media/dvb-frontends/mt352.c b/kernel/drivers/media/dvb-frontends/mt352.c index 2c3b50e82..123bb2f8e 100644 --- a/kernel/drivers/media/dvb-frontends/mt352.c +++ b/kernel/drivers/media/dvb-frontends/mt352.c @@ -417,7 +417,7 @@ static int mt352_get_parameters(struct dvb_frontend* fe) return 0; } -static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int mt352_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct mt352_state* state = fe->demodulator_priv; int s0, s1, s3; diff --git a/kernel/drivers/media/dvb-frontends/nxt200x.c b/kernel/drivers/media/dvb-frontends/nxt200x.c index 8a8e1ecb7..79c304091 100644 --- a/kernel/drivers/media/dvb-frontends/nxt200x.c +++ b/kernel/drivers/media/dvb-frontends/nxt200x.c @@ -781,7 +781,7 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) return 0; } -static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int nxt200x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct nxt200x_state* state = fe->demodulator_priv; u8 lock; diff --git a/kernel/drivers/media/dvb-frontends/nxt6000.c b/kernel/drivers/media/dvb-frontends/nxt6000.c index 90ae6c72c..73f950536 100644 --- a/kernel/drivers/media/dvb-frontends/nxt6000.c +++ b/kernel/drivers/media/dvb-frontends/nxt6000.c @@ -109,7 +109,8 @@ static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth) return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); } -static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval) +static int nxt6000_set_guard_interval(struct nxt6000_state *state, + enum fe_guard_interval guard_interval) { switch (guard_interval) { @@ -131,7 +132,8 @@ static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_inte } } -static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion) +static int nxt6000_set_inversion(struct nxt6000_state *state, + enum fe_spectral_inversion inversion) { switch (inversion) { @@ -147,7 +149,9 @@ static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_invers } } -static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode) +static int +nxt6000_set_transmission_mode(struct nxt6000_state *state, + enum fe_transmit_mode transmission_mode) { int result; @@ -416,7 +420,7 @@ static void nxt6000_dump_status(struct nxt6000_state *state) printk("\n"); } -static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int nxt6000_read_status(struct dvb_frontend *fe, enum fe_status *status) { u8 core_status; struct nxt6000_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/or51132.c b/kernel/drivers/media/dvb-frontends/or51132.c index cbbd259ea..35b1053b3 100644 --- a/kernel/drivers/media/dvb-frontends/or51132.c +++ b/kernel/drivers/media/dvb-frontends/or51132.c @@ -63,7 +63,7 @@ struct or51132_state struct dvb_frontend frontend; /* Demodulator private data */ - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 snr; /* Result of last SNR calculation */ /* Tuner private data */ @@ -292,7 +292,7 @@ static int or51132_setmode(struct dvb_frontend* fe) #define MOD_FWCLASS_UNKNOWN 0 #define MOD_FWCLASS_VSB 1 #define MOD_FWCLASS_QAM 2 -static int modulation_fw_class(fe_modulation_t modulation) +static int modulation_fw_class(enum fe_modulation modulation) { switch(modulation) { case VSB_8: @@ -415,7 +415,7 @@ start: return 0; } -static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int or51132_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct or51132_state* state = fe->demodulator_priv; int reg; diff --git a/kernel/drivers/media/dvb-frontends/or51211.c b/kernel/drivers/media/dvb-frontends/or51211.c index 873ea1da8..e82413b97 100644 --- a/kernel/drivers/media/dvb-frontends/or51211.c +++ b/kernel/drivers/media/dvb-frontends/or51211.c @@ -237,7 +237,7 @@ static int or51211_set_parameters(struct dvb_frontend *fe) return 0; } -static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int or51211_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct or51211_state* state = fe->demodulator_priv; unsigned char rec_buf[2]; diff --git a/kernel/drivers/media/dvb-frontends/rtl2830.c b/kernel/drivers/media/dvb-frontends/rtl2830.c index e1b8df62b..b792f305c 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2830.c +++ b/kernel/drivers/media/dvb-frontends/rtl2830.c @@ -392,7 +392,7 @@ err: return ret; } -static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct i2c_client *client = fe->demodulator_priv; struct rtl2830_dev *dev = i2c_get_clientdata(client); @@ -915,7 +915,6 @@ MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); static struct i2c_driver rtl2830_driver = { .driver = { - .owner = THIS_MODULE, .name = "rtl2830", }, .probe = rtl2830_probe, diff --git a/kernel/drivers/media/dvb-frontends/rtl2830_priv.h b/kernel/drivers/media/dvb-frontends/rtl2830_priv.h index d50d5376c..cf793f39a 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2830_priv.h +++ b/kernel/drivers/media/dvb-frontends/rtl2830_priv.h @@ -34,7 +34,7 @@ struct rtl2830_dev { bool sleeping; unsigned long filters; struct delayed_work stat_work; - fe_status_t fe_status; + enum fe_status fe_status; u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ u64 post_bit_error; u64 post_bit_count; diff --git a/kernel/drivers/media/dvb-frontends/rtl2832.c b/kernel/drivers/media/dvb-frontends/rtl2832.c index b400f7b3c..78b87b260 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2832.c +++ b/kernel/drivers/media/dvb-frontends/rtl2832.c @@ -358,6 +358,10 @@ static int rtl2832_init(struct dvb_frontend *fe) dev_dbg(&client->dev, "load settings for tuner=%02x\n", dev->pdata->tuner); switch (dev->pdata->tuner) { + case RTL2832_TUNER_FC2580: + len = ARRAY_SIZE(rtl2832_tuner_init_fc2580); + init = rtl2832_tuner_init_fc2580; + break; case RTL2832_TUNER_FC0012: case RTL2832_TUNER_FC0013: len = ARRAY_SIZE(rtl2832_tuner_init_fc0012); @@ -376,6 +380,10 @@ static int rtl2832_init(struct dvb_frontend *fe) len = ARRAY_SIZE(rtl2832_tuner_init_r820t); init = rtl2832_tuner_init_r820t; break; + case RTL2832_TUNER_SI2157: + len = ARRAY_SIZE(rtl2832_tuner_init_si2157); + init = rtl2832_tuner_init_si2157; + break; default: ret = -EINVAL; goto err; @@ -680,7 +688,7 @@ err: return ret; } -static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct rtl2832_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; @@ -1311,7 +1319,6 @@ MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); static struct i2c_driver rtl2832_driver = { .driver = { - .owner = THIS_MODULE, .name = "rtl2832", }, .probe = rtl2832_probe, diff --git a/kernel/drivers/media/dvb-frontends/rtl2832.h b/kernel/drivers/media/dvb-frontends/rtl2832.h index a8e912e67..c29a4c2bf 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2832.h +++ b/kernel/drivers/media/dvb-frontends/rtl2832.h @@ -41,12 +41,14 @@ struct rtl2832_platform_data { /* * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. */ +#define RTL2832_TUNER_FC2580 0x21 #define RTL2832_TUNER_TUA9001 0x24 #define RTL2832_TUNER_FC0012 0x26 #define RTL2832_TUNER_E4000 0x27 #define RTL2832_TUNER_FC0013 0x29 #define RTL2832_TUNER_R820T 0x2a #define RTL2832_TUNER_R828D 0x2b +#define RTL2832_TUNER_SI2157 0x2c u8 tuner; struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); diff --git a/kernel/drivers/media/dvb-frontends/rtl2832_priv.h b/kernel/drivers/media/dvb-frontends/rtl2832_priv.h index c3a922c37..5dcd3a41d 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/kernel/drivers/media/dvb-frontends/rtl2832_priv.h @@ -39,7 +39,7 @@ struct rtl2832_dev { struct i2c_adapter *i2c_adapter_tuner; struct dvb_frontend fe; struct delayed_work stat_work; - fe_status_t fe_status; + enum fe_status fe_status; u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ u64 post_bit_error; u64 post_bit_count; @@ -252,6 +252,30 @@ enum DVBT_REG_BIT_NAME { DVBT_REG_BIT_NAME_ITEM_TERMINATOR, }; +static const struct rtl2832_reg_value rtl2832_tuner_init_fc2580[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x5a}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x6}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x16}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x9c}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, +}; + static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = { {DVBT_DAGC_TRG_VAL, 0x39}, {DVBT_AGC_TARG_VAL_0, 0x0}, @@ -377,4 +401,29 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = { {DVBT_SPEC_INV, 0x1}, }; +static const struct rtl2832_reg_value rtl2832_tuner_init_si2157[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x40}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_SPEC_INV, 0x0}, +}; + #endif /* RTL2832_PRIV_H */ diff --git a/kernel/drivers/media/dvb-frontends/rtl2832_sdr.c b/kernel/drivers/media/dvb-frontends/rtl2832_sdr.c index 3ff8806ca..dcd8d94c1 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/kernel/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,10 @@ static bool rtl2832_sdr_emulated_fmt; module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644); MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)"); +/* Original macro does not contain enough null pointer checks for our need */ +#define V4L2_SUBDEV_HAS_OP(sd, o, f) \ + ((sd) && (sd)->ops && (sd)->ops->o && (sd)->ops->o->f) + #define MAX_BULK_BUFS (10) #define BULK_BUFFER_SIZE (128 * 512) @@ -103,19 +108,21 @@ static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); /* intermediate buffers with raw data from the USB device */ struct rtl2832_sdr_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; struct list_head list; }; struct rtl2832_sdr_dev { -#define POWER_ON (1 << 1) -#define URB_BUF (1 << 2) +#define POWER_ON 0 /* BIT(0) */ +#define URB_BUF 1 /* BIT(1) */ unsigned long flags; struct platform_device *pdev; struct video_device vdev; struct v4l2_device v4l2_dev; + struct v4l2_subdev *v4l2_subdev; /* videobuf2 queue and queued buffers list */ struct vb2_queue vb_queue; @@ -299,13 +306,13 @@ static void rtl2832_sdr_urb_complete(struct urb *urb) } /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); + ptr = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0); len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer, urb->actual_length); - vb2_set_plane_payload(&fbuf->vb, 0, len); - v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = dev->sequence++; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len); + v4l2_get_timestamp(&fbuf->vb.timestamp); + fbuf->vb.sequence = dev->sequence++; + vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); } skip: usb_submit_urb(urb, GFP_ATOMIC); @@ -351,7 +358,7 @@ static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev) { struct platform_device *pdev = dev->pdev; - if (dev->flags & USB_STATE_URB_BUF) { + if (test_bit(URB_BUF, &dev->flags)) { while (dev->buf_num) { dev->buf_num--; dev_dbg(&pdev->dev, "free buf=%d\n", dev->buf_num); @@ -360,7 +367,7 @@ static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev) dev->dma_addr[dev->buf_num]); } } - dev->flags &= ~USB_STATE_URB_BUF; + clear_bit(URB_BUF, &dev->flags); return 0; } @@ -389,7 +396,7 @@ static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev) dev_dbg(&pdev->dev, "alloc buf=%d %p (dma %llu)\n", dev->buf_num, dev->buf_list[dev->buf_num], (long long)dev->dma_addr[dev->buf_num]); - dev->flags |= USB_STATE_URB_BUF; + set_bit(URB_BUF, &dev->flags); } return 0; @@ -459,7 +466,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -483,7 +490,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh, /* Videobuf2 operations */ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); @@ -513,14 +520,15 @@ static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct rtl2832_sdr_frame_buf *buf = - container_of(vb, struct rtl2832_sdr_frame_buf, vb); + container_of(vbuf, struct rtl2832_sdr_frame_buf, vb); unsigned long flags; /* Check the device has not disconnected between prep and queuing */ if (!dev->udev) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); return; } @@ -742,6 +750,29 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1); break; + case RTL2832_SDR_TUNER_FC2580: + ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x39", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x9c", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xf4", 2); + break; default: dev_notice(&pdev->dev, "Unsupported tuner\n"); } @@ -832,8 +863,10 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev) if (!test_bit(POWER_ON, &dev->flags)) return 0; - if (fe->ops.tuner_ops.set_params) - fe->ops.tuner_ops.set_params(fe); + if (!V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) { + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + } return 0; }; @@ -891,7 +924,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) set_bit(POWER_ON, &dev->flags); - ret = rtl2832_sdr_set_tuner(dev); + /* wake-up tuner */ + if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power)) + ret = v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); + else + ret = rtl2832_sdr_set_tuner(dev); if (ret) goto err; @@ -939,7 +976,12 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) rtl2832_sdr_free_stream_bufs(dev); rtl2832_sdr_cleanup_queued_bufs(dev); rtl2832_sdr_unset_adc(dev); - rtl2832_sdr_unset_tuner(dev); + + /* sleep tuner */ + if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power)) + v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 0); + else + rtl2832_sdr_unset_tuner(dev); clear_bit(POWER_ON, &dev->flags); @@ -968,6 +1010,7 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv, { struct rtl2832_sdr_dev *dev = video_drvdata(file); struct platform_device *pdev = dev->pdev; + int ret; dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type); @@ -977,17 +1020,21 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv, v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; v->rangelow = 300000; v->rangehigh = 3200000; + ret = 0; + } else if (v->index == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_tuner)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_tuner, v); } else if (v->index == 1) { strlcpy(v->name, "RF: ", sizeof(v->name)); v->type = V4L2_TUNER_RF; v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; v->rangelow = 50000000; v->rangehigh = 2000000000; + ret = 0; } else { - return -EINVAL; + ret = -EINVAL; } - - return 0; + return ret; } static int rtl2832_sdr_s_tuner(struct file *file, void *priv, @@ -995,12 +1042,21 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv, { struct rtl2832_sdr_dev *dev = video_drvdata(file); struct platform_device *pdev = dev->pdev; + int ret; dev_dbg(&pdev->dev, "\n"); - if (v->index > 1) - return -EINVAL; - return 0; + if (v->index == 0) { + ret = 0; + } else if (v->index == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_tuner)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_tuner, v); + } else if (v->index == 1) { + ret = 0; + } else { + ret = -EINVAL; + } + return ret; } static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, @@ -1008,6 +1064,7 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, { struct rtl2832_sdr_dev *dev = video_drvdata(file); struct platform_device *pdev = dev->pdev; + int ret; dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n", band->tuner, band->type, band->index); @@ -1017,16 +1074,20 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, return -EINVAL; *band = bands_adc[band->index]; + ret = 0; + } else if (band->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, enum_freq_bands)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, enum_freq_bands, band); } else if (band->tuner == 1) { if (band->index >= ARRAY_SIZE(bands_fm)) return -EINVAL; *band = bands_fm[band->index]; + ret = 0; } else { - return -EINVAL; + ret = -EINVAL; } - - return 0; + return ret; } static int rtl2832_sdr_g_frequency(struct file *file, void *priv, @@ -1034,20 +1095,25 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv, { struct rtl2832_sdr_dev *dev = video_drvdata(file); struct platform_device *pdev = dev->pdev; - int ret = 0; + int ret; dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type); if (f->tuner == 0) { f->frequency = dev->f_adc; f->type = V4L2_TUNER_ADC; + ret = 0; + } else if (f->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_frequency)) { + f->type = V4L2_TUNER_RF; + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_frequency, f); } else if (f->tuner == 1) { f->frequency = dev->f_tuner; f->type = V4L2_TUNER_RF; + ret = 0; } else { - return -EINVAL; + ret = -EINVAL; } - return ret; } @@ -1074,11 +1140,14 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, band = 2; dev->f_adc = clamp_t(unsigned int, f->frequency, - bands_adc[band].rangelow, - bands_adc[band].rangehigh); + bands_adc[band].rangelow, + bands_adc[band].rangehigh); dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc); ret = rtl2832_sdr_set_adc(dev); + } else if (f->tuner == 1 && + V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) { + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_frequency, f); } else if (f->tuner == 1) { dev->f_tuner = clamp_t(unsigned int, f->frequency, bands_fm[0].rangelow, @@ -1089,7 +1158,6 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, } else { ret = -EINVAL; } - return ret; } @@ -1329,6 +1397,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev) /* setup the state */ subdev = pdata->v4l2_subdev; + dev->v4l2_subdev = pdata->v4l2_subdev; dev->pdev = pdev; dev->udev = pdata->dvb_usb_device->udev; dev->f_adc = bands_adc[0].rangelow; @@ -1388,6 +1457,12 @@ static int rtl2832_sdr_probe(struct platform_device *pdev) 6000000); v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); break; + case RTL2832_SDR_TUNER_FC2580: + v4l2_ctrl_handler_init(&dev->hdl, 2); + if (subdev) + v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, + NULL); + break; default: v4l2_ctrl_handler_init(&dev->hdl, 0); dev_err(&pdev->dev, "Unsupported tuner\n"); @@ -1466,7 +1541,6 @@ static int rtl2832_sdr_remove(struct platform_device *pdev) static struct platform_driver rtl2832_sdr_driver = { .driver = { .name = "rtl2832_sdr", - .owner = THIS_MODULE, }, .probe = rtl2832_sdr_probe, .remove = rtl2832_sdr_remove, diff --git a/kernel/drivers/media/dvb-frontends/rtl2832_sdr.h b/kernel/drivers/media/dvb-frontends/rtl2832_sdr.h index d2594768b..342ea8486 100644 --- a/kernel/drivers/media/dvb-frontends/rtl2832_sdr.h +++ b/kernel/drivers/media/dvb-frontends/rtl2832_sdr.h @@ -47,6 +47,7 @@ struct rtl2832_sdr_platform_data { /* * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. */ +#define RTL2832_SDR_TUNER_FC2580 0x21 #define RTL2832_SDR_TUNER_TUA9001 0x24 #define RTL2832_SDR_TUNER_FC0012 0x26 #define RTL2832_SDR_TUNER_E4000 0x27 diff --git a/kernel/drivers/media/dvb-frontends/s5h1409.c b/kernel/drivers/media/dvb-frontends/s5h1409.c index 5ff474a7f..10964848a 100644 --- a/kernel/drivers/media/dvb-frontends/s5h1409.c +++ b/kernel/drivers/media/dvb-frontends/s5h1409.c @@ -38,7 +38,7 @@ struct s5h1409_state { struct dvb_frontend frontend; /* previous uncorrected block counter */ - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; u32 current_frequency; int if_freq; @@ -400,7 +400,7 @@ static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted) } static int s5h1409_enable_modulation(struct dvb_frontend *fe, - fe_modulation_t m) + enum fe_modulation m) { struct s5h1409_state *state = fe->demodulator_priv; @@ -755,7 +755,7 @@ static int s5h1409_init(struct dvb_frontend *fe) return 0; } -static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int s5h1409_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct s5h1409_state *state = fe->demodulator_priv; u16 reg; diff --git a/kernel/drivers/media/dvb-frontends/s5h1411.c b/kernel/drivers/media/dvb-frontends/s5h1411.c index 64f35fed7..9afc3f422 100644 --- a/kernel/drivers/media/dvb-frontends/s5h1411.c +++ b/kernel/drivers/media/dvb-frontends/s5h1411.c @@ -37,7 +37,7 @@ struct s5h1411_state { struct dvb_frontend frontend; - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; unsigned int first_tune:1; u32 current_frequency; @@ -484,7 +484,7 @@ static int s5h1411_set_serialmode(struct dvb_frontend *fe, int serial) } static int s5h1411_enable_modulation(struct dvb_frontend *fe, - fe_modulation_t m) + enum fe_modulation m) { struct s5h1411_state *state = fe->demodulator_priv; @@ -659,7 +659,7 @@ static int s5h1411_init(struct dvb_frontend *fe) return 0; } -static int s5h1411_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int s5h1411_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct s5h1411_state *state = fe->demodulator_priv; u16 reg; diff --git a/kernel/drivers/media/dvb-frontends/s5h1420.c b/kernel/drivers/media/dvb-frontends/s5h1420.c index 0b4f8fe6b..9c22a4c70 100644 --- a/kernel/drivers/media/dvb-frontends/s5h1420.c +++ b/kernel/drivers/media/dvb-frontends/s5h1420.c @@ -52,7 +52,7 @@ struct s5h1420_state { u8 postlocked:1; u32 fclk; u32 tunedfreq; - fe_code_rate_t fec_inner; + enum fe_code_rate fec_inner; u32 symbol_rate; /* FIXME: ugly workaround for flexcop's incapable i2c-controller @@ -124,7 +124,8 @@ static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data) return 0; } -static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int s5h1420_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct s5h1420_state* state = fe->demodulator_priv; @@ -149,7 +150,8 @@ static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag return 0; } -static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int s5h1420_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct s5h1420_state* state = fe->demodulator_priv; @@ -270,7 +272,8 @@ exit: return result; } -static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int s5h1420_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct s5h1420_state* state = fe->demodulator_priv; u8 val; @@ -307,10 +310,10 @@ static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicm return result; } -static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state) +static enum fe_status s5h1420_get_status_bits(struct s5h1420_state *state) { u8 val; - fe_status_t status = 0; + enum fe_status status = 0; val = s5h1420_readreg(state, 0x14); if (val & 0x02) @@ -328,7 +331,8 @@ static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state) return status; } -static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int s5h1420_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct s5h1420_state* state = fe->demodulator_priv; u8 val; @@ -561,27 +565,33 @@ static void s5h1420_setfec_inversion(struct s5h1420_state* state, } else { switch (p->fec_inner) { case FEC_1_2: - vit08 = 0x01; vit09 = 0x10; + vit08 = 0x01; + vit09 = 0x10; break; case FEC_2_3: - vit08 = 0x02; vit09 = 0x11; + vit08 = 0x02; + vit09 = 0x11; break; case FEC_3_4: - vit08 = 0x04; vit09 = 0x12; + vit08 = 0x04; + vit09 = 0x12; break; case FEC_5_6: - vit08 = 0x08; vit09 = 0x13; + vit08 = 0x08; + vit09 = 0x13; break; case FEC_6_7: - vit08 = 0x10; vit09 = 0x14; + vit08 = 0x10; + vit09 = 0x14; break; case FEC_7_8: - vit08 = 0x20; vit09 = 0x15; + vit08 = 0x20; + vit09 = 0x15; break; default: @@ -595,7 +605,7 @@ static void s5h1420_setfec_inversion(struct s5h1420_state* state, dprintk("leave %s\n", __func__); } -static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state) +static enum fe_code_rate s5h1420_getfec(struct s5h1420_state *state) { switch(s5h1420_readreg(state, 0x32) & 0x07) { case 0: @@ -620,7 +630,8 @@ static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state) return FEC_NONE; } -static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state) +static enum fe_spectral_inversion +s5h1420_getinversion(struct s5h1420_state *state) { if (s5h1420_readreg(state, 0x32) & 0x08) return INVERSION_ON; diff --git a/kernel/drivers/media/dvb-frontends/s5h1432.c b/kernel/drivers/media/dvb-frontends/s5h1432.c index 6ec16a243..4215652f8 100644 --- a/kernel/drivers/media/dvb-frontends/s5h1432.c +++ b/kernel/drivers/media/dvb-frontends/s5h1432.c @@ -36,7 +36,7 @@ struct s5h1432_state { struct dvb_frontend frontend; - fe_modulation_t current_modulation; + enum fe_modulation current_modulation; unsigned int first_tune:1; u32 current_frequency; @@ -302,7 +302,7 @@ static int s5h1432_init(struct dvb_frontend *fe) return 0; } -static int s5h1432_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int s5h1432_read_status(struct dvb_frontend *fe, enum fe_status *status) { return 0; } diff --git a/kernel/drivers/media/dvb-frontends/s921.c b/kernel/drivers/media/dvb-frontends/s921.c index 69862e1fd..d6a8fa630 100644 --- a/kernel/drivers/media/dvb-frontends/s921.c +++ b/kernel/drivers/media/dvb-frontends/s921.c @@ -348,7 +348,7 @@ static int s921_initfe(struct dvb_frontend *fe) return 0; } -static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int s921_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct s921_state *state = fe->demodulator_priv; int regstatus, rc; @@ -389,7 +389,7 @@ static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status) static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - fe_status_t status; + enum fe_status status; struct s921_state *state = fe->demodulator_priv; int rc; @@ -449,7 +449,7 @@ static int s921_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { int rc = 0; @@ -466,7 +466,7 @@ static int s921_tune(struct dvb_frontend *fe, static int s921_get_algo(struct dvb_frontend *fe) { - return 1; /* FE_ALGO_HW */ + return DVBFE_ALGO_HW; } static void s921_release(struct dvb_frontend *fe) diff --git a/kernel/drivers/media/dvb-frontends/s921.h b/kernel/drivers/media/dvb-frontends/s921.h index 7d3999a4e..f5b722d80 100644 --- a/kernel/drivers/media/dvb-frontends/s921.h +++ b/kernel/drivers/media/dvb-frontends/s921.h @@ -36,7 +36,7 @@ static inline struct dvb_frontend *s921_attach( printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static struct i2c_adapter * +static inline struct i2c_adapter * s921_get_tuner_i2c_adapter(struct dvb_frontend *fe) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/kernel/drivers/media/dvb-frontends/si2165.c b/kernel/drivers/media/dvb-frontends/si2165.c index 4cc5d10ed..7c2eeee69 100644 --- a/kernel/drivers/media/dvb-frontends/si2165.c +++ b/kernel/drivers/media/dvb-frontends/si2165.c @@ -698,7 +698,7 @@ static int si2165_sleep(struct dvb_frontend *fe) return 0; } -static int si2165_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int si2165_read_status(struct dvb_frontend *fe, enum fe_status *status) { int ret; u8 fec_lock = 0; diff --git a/kernel/drivers/media/dvb-frontends/si2168.c b/kernel/drivers/media/dvb-frontends/si2168.c index 5db588ebf..821a8f481 100644 --- a/kernel/drivers/media/dvb-frontends/si2168.c +++ b/kernel/drivers/media/dvb-frontends/si2168.c @@ -18,23 +18,53 @@ static const struct dvb_frontend_ops si2168_ops; +/* Own I2C adapter locking is needed because of I2C gate logic. */ +static int si2168_i2c_master_send_unlocked(const struct i2c_client *client, + const char *buf, int count) +{ + int ret; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = count, + .buf = (char *)buf, + }; + + ret = __i2c_transfer(client->adapter, &msg, 1); + return (ret == 1) ? count : ret; +} + +static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client, + char *buf, int count) +{ + int ret; + struct i2c_msg msg = { + .addr = client->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }; + + ret = __i2c_transfer(client->adapter, &msg, 1); + return (ret == 1) ? count : ret; +} + /* execute firmware command */ -static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) +static int si2168_cmd_execute_unlocked(struct i2c_client *client, + struct si2168_cmd *cmd) { - struct si2168_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; - mutex_lock(&dev->i2c_mutex); - if (cmd->wlen) { /* write cmd and args for firmware */ - ret = i2c_master_send(client, cmd->args, cmd->wlen); + ret = si2168_i2c_master_send_unlocked(client, cmd->args, + cmd->wlen); if (ret < 0) { - goto err_mutex_unlock; + goto err; } else if (ret != cmd->wlen) { ret = -EREMOTEIO; - goto err_mutex_unlock; + goto err; } } @@ -43,12 +73,13 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) #define TIMEOUT 70 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = i2c_master_recv(client, cmd->args, cmd->rlen); + ret = si2168_i2c_master_recv_unlocked(client, cmd->args, + cmd->rlen); if (ret < 0) { - goto err_mutex_unlock; + goto err; } else if (ret != cmd->rlen) { ret = -EREMOTEIO; - goto err_mutex_unlock; + goto err; } /* firmware ready? */ @@ -60,22 +91,36 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - TIMEOUT)); + /* error bit set? */ + if ((cmd->args[0] >> 6) & 0x01) { + ret = -EREMOTEIO; + goto err; + } + if (!((cmd->args[0] >> 7) & 0x01)) { ret = -ETIMEDOUT; - goto err_mutex_unlock; + goto err; } } - mutex_unlock(&dev->i2c_mutex); return 0; - -err_mutex_unlock: - mutex_unlock(&dev->i2c_mutex); +err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) +{ + int ret; + + i2c_lock_adapter(client->adapter); + ret = si2168_cmd_execute_unlocked(client, cmd); + i2c_unlock_adapter(client->adapter); + + return ret; +} + +static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct i2c_client *client = fe->demodulator_priv; struct si2168_dev *dev = i2c_get_clientdata(client); @@ -457,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe) /* firmware is in the new format */ for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; + if (len > SI2168_ARGLEN) { + ret = -EINVAL; + break; + } memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; @@ -508,6 +557,8 @@ static int si2168_init(struct dvb_frontend *fe) /* set ts mode */ memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); cmd.args[4] |= dev->ts_mode; + if (dev->ts_clock_gapped) + cmd.args[4] |= 0x40; cmd.wlen = 6; cmd.rlen = 4; ret = si2168_cmd_execute(client, &cmd); @@ -561,60 +612,46 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, /* * I2C gate logic - * We must use unlocked i2c_transfer() here because I2C lock is already taken - * by tuner driver. + * We must use unlocked I2C I/O because I2C adapter lock is already taken + * by the caller (usually tuner driver). */ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) { struct i2c_client *client = mux_priv; - struct si2168_dev *dev = i2c_get_clientdata(client); int ret; - struct i2c_msg gate_open_msg = { - .addr = client->addr, - .flags = 0, - .len = 3, - .buf = "\xc0\x0d\x01", - }; - - mutex_lock(&dev->i2c_mutex); + struct si2168_cmd cmd; - /* open tuner I2C gate */ - ret = __i2c_transfer(client->adapter, &gate_open_msg, 1); - if (ret != 1) { - dev_warn(&client->dev, "i2c write failed=%d\n", ret); - if (ret >= 0) - ret = -EREMOTEIO; - } else { - ret = 0; - } + /* open I2C gate */ + memcpy(cmd.args, "\xc0\x0d\x01", 3); + cmd.wlen = 3; + cmd.rlen = 0; + ret = si2168_cmd_execute_unlocked(client, &cmd); + if (ret) + goto err; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) { struct i2c_client *client = mux_priv; - struct si2168_dev *dev = i2c_get_clientdata(client); int ret; - struct i2c_msg gate_close_msg = { - .addr = client->addr, - .flags = 0, - .len = 3, - .buf = "\xc0\x0d\x00", - }; - - /* close tuner I2C gate */ - ret = __i2c_transfer(client->adapter, &gate_close_msg, 1); - if (ret != 1) { - dev_warn(&client->dev, "i2c write failed=%d\n", ret); - if (ret >= 0) - ret = -EREMOTEIO; - } else { - ret = 0; - } + struct si2168_cmd cmd; - mutex_unlock(&dev->i2c_mutex); + /* close I2C gate */ + memcpy(cmd.args, "\xc0\x0d\x00", 3); + cmd.wlen = 3; + cmd.rlen = 0; + ret = si2168_cmd_execute_unlocked(client, &cmd); + if (ret) + goto err; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -671,8 +708,6 @@ static int si2168_probe(struct i2c_client *client, goto err; } - mutex_init(&dev->i2c_mutex); - /* create mux i2c adapter for tuner */ dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, client, 0, 0, 0, si2168_select, si2168_deselect); @@ -688,6 +723,7 @@ static int si2168_probe(struct i2c_client *client, *config->fe = &dev->fe; dev->ts_mode = config->ts_mode; dev->ts_clock_inv = config->ts_clock_inv; + dev->ts_clock_gapped = config->ts_clock_gapped; dev->fw_loaded = false; i2c_set_clientdata(client, dev); @@ -725,7 +761,6 @@ MODULE_DEVICE_TABLE(i2c, si2168_id_table); static struct i2c_driver si2168_driver = { .driver = { - .owner = THIS_MODULE, .name = "si2168", }, .probe = si2168_probe, diff --git a/kernel/drivers/media/dvb-frontends/si2168.h b/kernel/drivers/media/dvb-frontends/si2168.h index 70d702ae6..3225d0cc9 100644 --- a/kernel/drivers/media/dvb-frontends/si2168.h +++ b/kernel/drivers/media/dvb-frontends/si2168.h @@ -42,6 +42,9 @@ struct si2168_config { /* TS clock inverted */ bool ts_clock_inv; + + /* TS clock gapped */ + bool ts_clock_gapped; }; #endif diff --git a/kernel/drivers/media/dvb-frontends/si2168_priv.h b/kernel/drivers/media/dvb-frontends/si2168_priv.h index d7efce804..c07e6fe2c 100644 --- a/kernel/drivers/media/dvb-frontends/si2168_priv.h +++ b/kernel/drivers/media/dvb-frontends/si2168_priv.h @@ -30,14 +30,14 @@ /* state struct */ struct si2168_dev { struct i2c_adapter *adapter; - struct mutex i2c_mutex; struct dvb_frontend fe; - fe_delivery_system_t delivery_system; - fe_status_t fe_status; + enum fe_delivery_system delivery_system; + enum fe_status fe_status; bool active; bool fw_loaded; u8 ts_mode; bool ts_clock_inv; + bool ts_clock_gapped; }; /* firmware command struct */ diff --git a/kernel/drivers/media/dvb-frontends/si21xx.c b/kernel/drivers/media/dvb-frontends/si21xx.c index 16850e2bf..62ad7a7be 100644 --- a/kernel/drivers/media/dvb-frontends/si21xx.c +++ b/kernel/drivers/media/dvb-frontends/si21xx.c @@ -410,7 +410,7 @@ static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, } static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct si21xx_state *state = fe->demodulator_priv; u8 val; @@ -434,7 +434,7 @@ static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, return 0; } /* 30.06.2008 */ -static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct si21xx_state *state = fe->demodulator_priv; u8 val; @@ -454,7 +454,7 @@ static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) } } -static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) { struct si21xx_state *state = fe->demodulator_priv; @@ -536,7 +536,7 @@ static int si21xx_init(struct dvb_frontend *fe) } -static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct si21xx_state *state = fe->demodulator_priv; u8 regs_read[2]; @@ -641,7 +641,7 @@ static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) /* initiates a channel acquisition sequence using the specified symbol rate and code rate */ static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, - fe_code_rate_t crate) + enum fe_code_rate crate) { struct si21xx_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/sp2.c b/kernel/drivers/media/dvb-frontends/sp2.c index 8fd42767e..43d47dfcc 100644 --- a/kernel/drivers/media/dvb-frontends/sp2.c +++ b/kernel/drivers/media/dvb-frontends/sp2.c @@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(i2c, sp2_id); static struct i2c_driver sp2_driver = { .driver = { - .owner = THIS_MODULE, .name = "sp2", }, .probe = sp2_probe, diff --git a/kernel/drivers/media/dvb-frontends/sp8870.c b/kernel/drivers/media/dvb-frontends/sp8870.c index 57dc2abaa..e87ac30d7 100644 --- a/kernel/drivers/media/dvb-frontends/sp8870.c +++ b/kernel/drivers/media/dvb-frontends/sp8870.c @@ -350,7 +350,8 @@ static int sp8870_init (struct dvb_frontend* fe) return 0; } -static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status) +static int sp8870_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) { struct sp8870_state* state = fe->demodulator_priv; int status; diff --git a/kernel/drivers/media/dvb-frontends/sp887x.c b/kernel/drivers/media/dvb-frontends/sp887x.c index 1bb81b5ae..4378fe1b9 100644 --- a/kernel/drivers/media/dvb-frontends/sp887x.c +++ b/kernel/drivers/media/dvb-frontends/sp887x.c @@ -416,7 +416,7 @@ static int sp887x_setup_frontend_parameters(struct dvb_frontend *fe) return 0; } -static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int sp887x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct sp887x_state* state = fe->demodulator_priv; u16 snr12 = sp887x_readreg(state, 0xf16); diff --git a/kernel/drivers/media/dvb-frontends/stb0899_drv.c b/kernel/drivers/media/dvb-frontends/stb0899_drv.c index c73899d3a..756650f15 100644 --- a/kernel/drivers/media/dvb-frontends/stb0899_drv.c +++ b/kernel/drivers/media/dvb-frontends/stb0899_drv.c @@ -792,7 +792,8 @@ static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) return 0; } -static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct stb0899_state *state = fe->demodulator_priv; u8 reg, old_state; @@ -1178,7 +1179,8 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) return 0; } -static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int stb0899_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct stb0899_state *state = fe->demodulator_priv; @@ -1205,7 +1207,7 @@ static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage return 0; } -static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int stb0899_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct stb0899_state *state = fe->demodulator_priv; struct stb0899_internal *internal = &state->internal; diff --git a/kernel/drivers/media/dvb-frontends/stv0288.c b/kernel/drivers/media/dvb-frontends/stv0288.c index 632b25156..c93d9a45f 100644 --- a/kernel/drivers/media/dvb-frontends/stv0288.c +++ b/kernel/drivers/media/dvb-frontends/stv0288.c @@ -44,7 +44,7 @@ struct stv0288_state { u8 initialised:1; u32 tuner_frequency; u32 symbol_rate; - fe_code_rate_t fec_inner; + enum fe_code_rate fec_inner; int errmode; }; @@ -134,20 +134,20 @@ static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) temp = (unsigned int)srate / 1000; - temp = temp * 32768; - temp = temp / 25; - temp = temp / 125; - b[0] = (unsigned char)((temp >> 12) & 0xff); - b[1] = (unsigned char)((temp >> 4) & 0xff); - b[2] = (unsigned char)((temp << 4) & 0xf0); - stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ - stv0288_writeregI(state, 0x29, 0); /* SFRM */ - stv0288_writeregI(state, 0x2a, 0); /* SFRL */ - - stv0288_writeregI(state, 0x28, b[0]); - stv0288_writeregI(state, 0x29, b[1]); - stv0288_writeregI(state, 0x2a, b[2]); - dprintk("stv0288: stv0288_set_symbolrate\n"); + temp = temp * 32768; + temp = temp / 25; + temp = temp / 125; + b[0] = (unsigned char)((temp >> 12) & 0xff); + b[1] = (unsigned char)((temp >> 4) & 0xff); + b[2] = (unsigned char)((temp << 4) & 0xf0); + stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ + stv0288_writeregI(state, 0x29, 0); /* SFRM */ + stv0288_writeregI(state, 0x2a, 0); /* SFRL */ + + stv0288_writeregI(state, 0x28, b[0]); + stv0288_writeregI(state, 0x29, b[1]); + stv0288_writeregI(state, 0x2a, b[2]); + dprintk("stv0288: stv0288_set_symbolrate\n"); return 0; } @@ -174,7 +174,7 @@ static int stv0288_send_diseqc_msg(struct dvb_frontend *fe, } static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t burst) + enum fe_sec_mini_cmd burst) { struct stv0288_state *state = fe->demodulator_priv; @@ -193,7 +193,7 @@ static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, return 0; } -static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int stv0288_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct stv0288_state *state = fe->demodulator_priv; @@ -323,7 +323,8 @@ static u8 stv0288_inittab[] = { 0xff, 0xff, }; -static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +static int stv0288_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage volt) { dprintk("%s: %s\n", __func__, volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : @@ -361,7 +362,7 @@ static int stv0288_init(struct dvb_frontend *fe) return 0; } -static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct stv0288_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/stv0297.c b/kernel/drivers/media/dvb-frontends/stv0297.c index d40f22616..75b4d8b25 100644 --- a/kernel/drivers/media/dvb-frontends/stv0297.c +++ b/kernel/drivers/media/dvb-frontends/stv0297.c @@ -136,10 +136,10 @@ static u32 stv0297_get_symbolrate(struct stv0297_state *state) { u64 tmp; - tmp = stv0297_readreg(state, 0x55); - tmp |= stv0297_readreg(state, 0x56) << 8; - tmp |= stv0297_readreg(state, 0x57) << 16; - tmp |= stv0297_readreg(state, 0x58) << 24; + tmp = (u64)(stv0297_readreg(state, 0x55) + | (stv0297_readreg(state, 0x56) << 8) + | (stv0297_readreg(state, 0x57) << 16) + | (stv0297_readreg(state, 0x58) << 24)); tmp *= STV0297_CLOCK_KHZ; tmp >>= 32; @@ -233,7 +233,8 @@ static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq) stv0297_writereg(state, 0x20, tmp); } -static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulation) +static int stv0297_set_qam(struct stv0297_state *state, + enum fe_modulation modulation) { int val = 0; @@ -267,7 +268,8 @@ static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulati return 0; } -static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_inversion_t inversion) +static int stv0297_set_inversion(struct stv0297_state *state, + enum fe_spectral_inversion inversion) { int val = 0; @@ -325,7 +327,8 @@ static int stv0297_sleep(struct dvb_frontend *fe) return 0; } -static int stv0297_read_status(struct dvb_frontend *fe, fe_status_t * status) +static int stv0297_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct stv0297_state *state = fe->demodulator_priv; @@ -415,7 +418,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe) int sweeprate; int carrieroffset; unsigned long timeout; - fe_spectral_inversion_t inversion; + enum fe_spectral_inversion inversion; switch (p->modulation) { case QAM_16: diff --git a/kernel/drivers/media/dvb-frontends/stv0299.c b/kernel/drivers/media/dvb-frontends/stv0299.c index b57ecf42e..a8177807f 100644 --- a/kernel/drivers/media/dvb-frontends/stv0299.c +++ b/kernel/drivers/media/dvb-frontends/stv0299.c @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,7 @@ struct stv0299_state { u8 initialised:1; u32 tuner_frequency; u32 symbol_rate; - fe_code_rate_t fec_inner; + enum fe_code_rate fec_inner; int errmode; u32 ucblocks; u8 mcr_reg; @@ -134,7 +135,7 @@ static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len return ret == 2 ? 0 : ret; } -static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) +static int stv0299_set_FEC(struct stv0299_state *state, enum fe_code_rate fec) { dprintk ("%s\n", __func__); @@ -170,10 +171,10 @@ static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) } } -static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state) +static enum fe_code_rate stv0299_get_fec(struct stv0299_state *state) { - static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6, - FEC_7_8, FEC_1_2 }; + static enum fe_code_rate fec_tab[] = { FEC_2_3, FEC_3_4, FEC_5_6, + FEC_7_8, FEC_1_2 }; u8 index; dprintk ("%s\n", __func__); @@ -302,7 +303,8 @@ static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, return 0; } -static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +static int stv0299_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct stv0299_state* state = fe->demodulator_priv; u8 val; @@ -329,7 +331,8 @@ static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t return 0; } -static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int stv0299_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct stv0299_state* state = fe->demodulator_priv; u8 val; @@ -351,7 +354,8 @@ static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) } } -static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int stv0299_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct stv0299_state* state = fe->demodulator_priv; u8 reg0x08; @@ -404,8 +408,8 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long u8 lv_mask = 0x40; u8 last = 1; int i; - struct timeval nexttime; - struct timeval tv[10]; + ktime_t nexttime; + ktime_t tv[10]; reg0x08 = stv0299_readreg (state, 0x08); reg0x0c = stv0299_readreg (state, 0x0c); @@ -418,7 +422,7 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long if (debug_legacy_dish_switch) printk ("%s switch command: 0x%04lx\n",__func__, cmd); - do_gettimeofday (&nexttime); + nexttime = ktime_get_real(); if (debug_legacy_dish_switch) tv[0] = nexttime; stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ @@ -427,7 +431,7 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long for (i=0; i<9; i++) { if (debug_legacy_dish_switch) - do_gettimeofday (&tv[i+1]); + tv[i+1] = ktime_get_real(); if((cmd & 0x01) != last) { /* set voltage to (last ? 13V : 18V) */ stv0299_writeregI (state, 0x0c, reg0x0c | (last ? lv_mask : 0x50)); @@ -443,7 +447,8 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long printk ("%s(%d): switch delay (should be 32k followed by all 8k\n", __func__, fe->dvb->num); for (i = 1; i < 10; i++) - printk ("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); + printk("%d: %d\n", i, + (int) ktime_us_delta(tv[i], tv[i-1])); } return 0; @@ -476,7 +481,8 @@ static int stv0299_init (struct dvb_frontend* fe) return 0; } -static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int stv0299_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct stv0299_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/stv0367.c b/kernel/drivers/media/dvb-frontends/stv0367.c index b31ff265f..44cb73f68 100644 --- a/kernel/drivers/media/dvb-frontends/stv0367.c +++ b/kernel/drivers/media/dvb-frontends/stv0367.c @@ -59,7 +59,7 @@ struct stv0367cab_state { int locked; /* channel found */ u32 freq_khz; /* found frequency (in kHz) */ u32 symbol_rate; /* found symbol rate (in Bds) */ - fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */ + enum fe_spectral_inversion spect_inv; /* Spectrum Inversion */ }; struct stv0367ter_state { @@ -67,10 +67,10 @@ struct stv0367ter_state { enum stv0367_ter_signal_type state; enum stv0367_ter_if_iq_mode if_iq_mode; enum stv0367_ter_mode mode;/* mode 2K or 8K */ - fe_guard_interval_t guard; + enum fe_guard_interval guard; enum stv0367_ter_hierarchy hierarchy; u32 frequency; - fe_spectral_inversion_t sense; /* current search spectrum */ + enum fe_spectral_inversion sense; /* current search spectrum */ u8 force; /* force mode/guard */ u8 bw; /* channel width 6, 7 or 8 in MHz */ u8 pBW; /* channel width used during previous lock */ @@ -791,11 +791,13 @@ int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) memcpy(buf + 2, data, len); if (i2cdebug) - printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]); + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, buf[2]); ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) - printk(KERN_ERR "%s: i2c write error!\n", __func__); + printk(KERN_ERR "%s: i2c write error! ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, buf[2]); return (ret != 1) ? -EREMOTEIO : 0; } @@ -829,10 +831,12 @@ static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) - printk(KERN_ERR "%s: i2c read error\n", __func__); + printk(KERN_ERR "%s: i2c read error ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, b1[0]); if (i2cdebug) - printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]); + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, b1[0]); return b1[0]; } @@ -1550,6 +1554,11 @@ static int stv0367ter_init(struct dvb_frontend *fe) 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); @@ -2074,7 +2083,8 @@ static int stv0367ter_status(struct dvb_frontend *fe) return locked; } #endif -static int stv0367ter_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int stv0367ter_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct stv0367_state *state = fe->demodulator_priv; @@ -2716,7 +2726,8 @@ static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz) return regsym; } -static int stv0367cab_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int stv0367cab_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct stv0367_state *state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/stv0367_priv.h b/kernel/drivers/media/dvb-frontends/stv0367_priv.h index 995db0689..89bf6f64b 100644 --- a/kernel/drivers/media/dvb-frontends/stv0367_priv.h +++ b/kernel/drivers/media/dvb-frontends/stv0367_priv.h @@ -188,7 +188,7 @@ struct stv0367_cab_signal_info { u32 frequency; /* kHz */ u32 symbol_rate; /* Mbds */ enum stv0367cab_mod modulation; - fe_spectral_inversion_t spect_inv; + enum fe_spectral_inversion spect_inv; s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */ u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */ u32 BER; /* Bit error rate (x 10000000) */ diff --git a/kernel/drivers/media/dvb-frontends/stv0900_core.c b/kernel/drivers/media/dvb-frontends/stv0900_core.c index 2c88abfab..fe31dd541 100644 --- a/kernel/drivers/media/dvb-frontends/stv0900_core.c +++ b/kernel/drivers/media/dvb-frontends/stv0900_core.c @@ -1744,7 +1744,8 @@ static int stv0900_send_master_cmd(struct dvb_frontend *fe, state->demod); } -static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +static int stv0900_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct stv0900_state *state = fe->demodulator_priv; struct stv0900_internal *intp = state->internal; @@ -1793,7 +1794,8 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe, return 0; } -static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff) +static int stv0900_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode toneoff) { struct stv0900_state *state = fe->demodulator_priv; struct stv0900_internal *intp = state->internal; diff --git a/kernel/drivers/media/dvb-frontends/stv0900_sw.c b/kernel/drivers/media/dvb-frontends/stv0900_sw.c index a0a7b1664..fa63a9e92 100644 --- a/kernel/drivers/media/dvb-frontends/stv0900_sw.c +++ b/kernel/drivers/media/dvb-frontends/stv0900_sw.c @@ -1556,8 +1556,8 @@ static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) } symbcomp = 13 * (coarse_srate / 10); - coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) - | stv0900_read_reg(intp, CFR1); + coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); if (symbcomp < intp->symbol_rate[demod]) coarse_srate = 0; @@ -2009,7 +2009,7 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) signal_type = STV0900_NODATA; no_signal = stv0900_check_signal_presence(intp, demod); - intp->result[demod].locked = FALSE; + intp->result[demod].locked = FALSE; } } diff --git a/kernel/drivers/media/dvb-frontends/stv090x.c b/kernel/drivers/media/dvb-frontends/stv090x.c index 0b2a934f5..25bdf6e0f 100644 --- a/kernel/drivers/media/dvb-frontends/stv090x.c +++ b/kernel/drivers/media/dvb-frontends/stv090x.c @@ -3732,7 +3732,7 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) return 0; } -static int stv090x_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int stv090x_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct stv090x_state *state = fe->demodulator_priv; u32 reg; @@ -3822,7 +3822,8 @@ err: return -1; } -static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct stv090x_state *state = fe->demodulator_priv; u32 reg, idle = 0, fifo_full = 1; diff --git a/kernel/drivers/media/dvb-frontends/stv6110.c b/kernel/drivers/media/dvb-frontends/stv6110.c index b1425830a..91c6dcf65 100644 --- a/kernel/drivers/media/dvb-frontends/stv6110.c +++ b/kernel/drivers/media/dvb-frontends/stv6110.c @@ -158,7 +158,7 @@ static int stv6110_sleep(struct dvb_frontend *fe) return 0; } -static u32 carrier_width(u32 symbol_rate, fe_rolloff_t rolloff) +static u32 carrier_width(u32 symbol_rate, enum fe_rolloff rolloff) { u32 rlf; diff --git a/kernel/drivers/media/dvb-frontends/tc90522.c b/kernel/drivers/media/dvb-frontends/tc90522.c index dce22ce35..456cdc7fb 100644 --- a/kernel/drivers/media/dvb-frontends/tc90522.c +++ b/kernel/drivers/media/dvb-frontends/tc90522.c @@ -130,7 +130,7 @@ static int tc90522t_set_layers(struct dvb_frontend *fe) /* frontend ops */ -static int tc90522s_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int tc90522s_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct tc90522_state *state; int ret; @@ -158,7 +158,7 @@ static int tc90522s_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -static int tc90522t_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int tc90522t_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct tc90522_state *state; int ret; @@ -194,7 +194,7 @@ static int tc90522t_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -static const fe_code_rate_t fec_conv_sat[] = { +static const enum fe_code_rate fec_conv_sat[] = { FEC_NONE, /* unused */ FEC_1_2, /* for BPSK */ FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, /* for QPSK */ @@ -238,7 +238,10 @@ static int tc90522s_get_frontend(struct dvb_frontend *fe) c->layer[1].segment_count = 0; else c->layer[1].segment_count = val[4] & 0x3f; /* slots */ - /* actually, BPSK if v==1, but not defined in fe_modulation_t */ + /* + * actually, BPSK if v==1, but not defined in + * enum fe_modulation + */ c->layer[1].modulation = QPSK; layers = (v > 0) ? 2 : 1; } @@ -319,18 +322,18 @@ static int tc90522s_get_frontend(struct dvb_frontend *fe) } -static const fe_transmit_mode_t tm_conv[] = { +static const enum fe_transmit_mode tm_conv[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_8K, 0 }; -static const fe_code_rate_t fec_conv_ter[] = { +static const enum fe_code_rate fec_conv_ter[] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, 0, 0, 0 }; -static const fe_modulation_t mod_conv[] = { +static const enum fe_modulation mod_conv[] = { DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0 }; diff --git a/kernel/drivers/media/dvb-frontends/tda10021.c b/kernel/drivers/media/dvb-frontends/tda10021.c index 1bff7f457..a684424e6 100644 --- a/kernel/drivers/media/dvb-frontends/tda10021.c +++ b/kernel/drivers/media/dvb-frontends/tda10021.c @@ -129,8 +129,8 @@ static int unlock_tuner(struct tda10021_state* state) return 0; } -static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, - fe_spectral_inversion_t inversion) +static int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0, + enum fe_spectral_inversion inversion) { reg0 |= state->reg0 & 0x63; @@ -258,7 +258,7 @@ static int tda10021_set_parameters(struct dvb_frontend *fe) } /* - * gcc optimizes the code bellow the same way as it would code: + * gcc optimizes the code below the same way as it would code: * "if (qam > 5) return -EINVAL;" * Yet, the code is clearer, as it shows what QAM standards are * supported by the driver, and avoids the usage of magic numbers on @@ -308,7 +308,8 @@ static int tda10021_set_parameters(struct dvb_frontend *fe) return 0; } -static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int tda10021_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct tda10021_state* state = fe->demodulator_priv; int sync; diff --git a/kernel/drivers/media/dvb-frontends/tda10023.c b/kernel/drivers/media/dvb-frontends/tda10023.c index ca1e0d54b..44a556560 100644 --- a/kernel/drivers/media/dvb-frontends/tda10023.c +++ b/kernel/drivers/media/dvb-frontends/tda10023.c @@ -331,7 +331,7 @@ static int tda10023_set_parameters(struct dvb_frontend *fe) } /* - * gcc optimizes the code bellow the same way as it would code: + * gcc optimizes the code below the same way as it would code: * "if (qam > 5) return -EINVAL;" * Yet, the code is clearer, as it shows what QAM standards are * supported by the driver, and avoids the usage of magic numbers on @@ -376,7 +376,8 @@ static int tda10023_set_parameters(struct dvb_frontend *fe) return 0; } -static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int tda10023_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct tda10023_state* state = fe->demodulator_priv; int sync; diff --git a/kernel/drivers/media/dvb-frontends/tda10048.c b/kernel/drivers/media/dvb-frontends/tda10048.c index 71fb63299..8451086c5 100644 --- a/kernel/drivers/media/dvb-frontends/tda10048.c +++ b/kernel/drivers/media/dvb-frontends/tda10048.c @@ -792,7 +792,7 @@ static int tda10048_init(struct dvb_frontend *fe) return ret; } -static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int tda10048_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct tda10048_state *state = fe->demodulator_priv; u8 reg; diff --git a/kernel/drivers/media/dvb-frontends/tda1004x.c b/kernel/drivers/media/dvb-frontends/tda1004x.c index a2631be7f..c6abeb4fb 100644 --- a/kernel/drivers/media/dvb-frontends/tda1004x.c +++ b/kernel/drivers/media/dvb-frontends/tda1004x.c @@ -650,7 +650,7 @@ static int tda10046_init(struct dvb_frontend* fe) if (tda10046_fwupload(fe)) { printk("tda1004x: firmware upload failed\n"); - return -EIO; + return -EIO; } // tda setup @@ -903,9 +903,18 @@ static int tda1004x_get_fe(struct dvb_frontend *fe) { struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; struct tda1004x_state* state = fe->demodulator_priv; + int status; dprintk("%s\n", __func__); + status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); + if (status == -1) + return -EIO; + + /* Only update the properties cache if device is locked */ + if (!(status & 8)) + return 0; + // inversion status fe_params->inversion = INVERSION_OFF; if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) @@ -1005,7 +1014,8 @@ static int tda1004x_get_fe(struct dvb_frontend *fe) return 0; } -static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status) +static int tda1004x_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) { struct tda1004x_state* state = fe->demodulator_priv; int status; diff --git a/kernel/drivers/media/dvb-frontends/tda10071.c b/kernel/drivers/media/dvb-frontends/tda10071.c index 4a19b8599..119d47596 100644 --- a/kernel/drivers/media/dvb-frontends/tda10071.c +++ b/kernel/drivers/media/dvb-frontends/tda10071.c @@ -20,102 +20,15 @@ #include "tda10071_priv.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - static struct dvb_frontend_ops tda10071_ops; -/* write multiple registers */ -static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, - int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg.demod_i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (1 + 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; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple registers */ -static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, - int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg.demod_i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg.demod_i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (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; - } - - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* write single register */ -static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val) -{ - return tda10071_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val) -{ - return tda10071_rd_regs(priv, reg, val, 1); -} - +/* + * XXX: regmap_update_bits() does not fit our needs as it does not support + * partially volatile registers. Also it performs register read even mask is as + * wide as register value. + */ /* write single register with mask */ -static int tda10071_wr_reg_mask(struct tda10071_priv *priv, +static int tda10071_wr_reg_mask(struct tda10071_dev *dev, u8 reg, u8 val, u8 mask) { int ret; @@ -123,7 +36,7 @@ static int tda10071_wr_reg_mask(struct tda10071_priv *priv, /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = tda10071_rd_regs(priv, reg, &tmp, 1); + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); if (ret) return ret; @@ -132,64 +45,45 @@ static int tda10071_wr_reg_mask(struct tda10071_priv *priv, val |= tmp; } - return tda10071_wr_regs(priv, reg, &val, 1); -} - -/* read single register with mask */ -static int tda10071_rd_reg_mask(struct tda10071_priv *priv, - u8 reg, u8 *val, u8 mask) -{ - int ret, i; - u8 tmp; - - ret = tda10071_rd_regs(priv, reg, &tmp, 1); - if (ret) - return ret; - - tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; - } - *val = tmp >> i; - - return 0; + return regmap_bulk_write(dev->regmap, reg, &val, 1); } /* execute firmware command */ -static int tda10071_cmd_execute(struct tda10071_priv *priv, +static int tda10071_cmd_execute(struct tda10071_dev *dev, struct tda10071_cmd *cmd) { + struct i2c_client *client = dev->client; int ret, i; - u8 tmp; + unsigned int uitmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } + mutex_lock(&dev->cmd_execute_mutex); + /* write cmd and args for firmware */ - ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len); + ret = regmap_bulk_write(dev->regmap, 0x00, cmd->args, cmd->len); if (ret) - goto error; + goto error_mutex_unlock; /* start cmd execution */ - ret = tda10071_wr_reg(priv, 0x1f, 1); + ret = regmap_write(dev->regmap, 0x1f, 1); if (ret) - goto error; + goto error_mutex_unlock; /* wait cmd execution terminate */ - for (i = 1000, tmp = 1; i && tmp; i--) { - ret = tda10071_rd_reg(priv, 0x1f, &tmp); + for (i = 1000, uitmp = 1; i && uitmp; i--) { + ret = regmap_read(dev->regmap, 0x1f, &uitmp); if (ret) - goto error; + goto error_mutex_unlock; usleep_range(200, 5000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + mutex_unlock(&dev->cmd_execute_mutex); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -197,26 +91,28 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv, } return ret; +error_mutex_unlock: + mutex_unlock(&dev->cmd_execute_mutex); error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t fe_sec_tone_mode) + enum fe_sec_tone_mode fe_sec_tone_mode) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; u8 tone; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } - dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__, - fe_sec_tone_mode); + dev_dbg(&client->dev, "tone_mode=%d\n", fe_sec_tone_mode); switch (fe_sec_tone_mode) { case SEC_TONE_ON: @@ -226,8 +122,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, tone = 0; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n"); ret = -EINVAL; goto error; } @@ -238,30 +133,31 @@ static int tda10071_set_tone(struct dvb_frontend *fe, cmd.args[3] = 0x00; cmd.args[4] = tone; cmd.len = 5; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t fe_sec_voltage) + enum fe_sec_voltage fe_sec_voltage) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; u8 voltage; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } - dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage); + dev_dbg(&client->dev, "voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_13: @@ -274,8 +170,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, voltage = 0; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_voltage\n"); ret = -EINVAL; goto error; } @@ -284,31 +179,31 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, cmd.args[1] = 0; cmd.args[2] = voltage; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *diseqc_cmd) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp; + unsigned int uitmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } - dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__, - diseqc_cmd->msg_len); + dev_dbg(&client->dev, "msg_len=%d\n", diseqc_cmd->msg_len); if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { ret = -EINVAL; @@ -316,22 +211,22 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, } /* wait LNB TX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 0) & 1; usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; goto error; } - ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); if (ret) goto error; @@ -344,41 +239,42 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, cmd.args[6] = diseqc_cmd->msg_len; memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len); cmd.len = 7 + diseqc_cmd->msg_len; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp; + unsigned int uitmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); /* wait LNB RX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 1) & 1; usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -386,11 +282,11 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, } /* reply len */ - ret = tda10071_rd_reg(priv, 0x46, &tmp); + ret = regmap_read(dev->regmap, 0x46, &uitmp); if (ret) goto error; - reply->msg_len = tmp & 0x1f; /* [4:0] */ + reply->msg_len = uitmp & 0x1f; /* [4:0] */ if (reply->msg_len > sizeof(reply->msg)) reply->msg_len = sizeof(reply->msg); /* truncate API max */ @@ -398,35 +294,37 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, cmd.args[0] = CMD_LNB_UPDATE_REPLY; cmd.args[1] = 0; cmd.len = 2; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len); + ret = regmap_bulk_read(dev->regmap, cmd.len, reply->msg, + reply->msg_len); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t fe_sec_mini_cmd) + enum fe_sec_mini_cmd fe_sec_mini_cmd) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp, burst; + unsigned int uitmp; + u8 burst; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } - dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, - fe_sec_mini_cmd); + dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd); switch (fe_sec_mini_cmd) { case SEC_MINI_A: @@ -436,29 +334,28 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, burst = 1; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n"); ret = -EINVAL; goto error; } /* wait LNB TX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 0) & 1; usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; goto error; } - ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); if (ret) goto error; @@ -466,219 +363,217 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, cmd.args[1] = 0; cmd.args[2] = burst; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda10071_cmd cmd; int ret; - u8 tmp; + unsigned int uitmp; + u8 buf[8]; *status = 0; - if (!priv->warm) { + if (!dev->warm) { ret = 0; goto error; } - ret = tda10071_rd_reg(priv, 0x39, &tmp); + ret = regmap_read(dev->regmap, 0x39, &uitmp); if (ret) goto error; /* 0x39[0] tuner PLL */ - if (tmp & 0x02) /* demod PLL */ + if (uitmp & 0x02) /* demod PLL */ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; - if (tmp & 0x04) /* viterbi or LDPC*/ + if (uitmp & 0x04) /* viterbi or LDPC*/ *status |= FE_HAS_VITERBI; - if (tmp & 0x08) /* RS or BCH */ + if (uitmp & 0x08) /* RS or BCH */ *status |= FE_HAS_SYNC | FE_HAS_LOCK; - priv->fe_status = *status; + dev->fe_status = *status; - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + cmd.args[0] = CMD_GET_AGCACC; + cmd.args[1] = 0; + cmd.len = 2; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; -static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct tda10071_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; + /* input power estimate dBm */ + ret = regmap_read(dev->regmap, 0x50, &uitmp); + if (ret) + goto error; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - *snr = 0; - ret = 0; - goto error; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = (int) (uitmp - 256) * 1000; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - ret = tda10071_rd_regs(priv, 0x3a, buf, 2); - if (ret) - goto error; + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + /* Es/No */ + ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2); + if (ret) + goto error; - /* Es/No dBx10 */ - *snr = buf[0] << 8 | buf[1]; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = (buf[0] << 8 | buf[1] << 0) * 100; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* TODO: report total bits/packets */ + u8 delivery_system, reg, len; -static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct tda10071_priv *priv = fe->demodulator_priv; - struct tda10071_cmd cmd; - int ret; - u8 tmp; + switch (dev->delivery_system) { + case SYS_DVBS: + reg = 0x4c; + len = 8; + delivery_system = 1; + break; + case SYS_DVBS2: + reg = 0x4d; + len = 4; + delivery_system = 0; + break; + default: + ret = -EINVAL; + goto error; + } - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - *strength = 0; - ret = 0; - goto error; - } + ret = regmap_read(dev->regmap, reg, &uitmp); + if (ret) + goto error; - cmd.args[0] = CMD_GET_AGCACC; - cmd.args[1] = 0; - cmd.len = 2; - ret = tda10071_cmd_execute(priv, &cmd); - if (ret) - goto error; + if (dev->meas_count == uitmp) { + dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp); + ret = 0; + goto error; + } else { + dev->meas_count = uitmp; + } - /* input power estimate dBm */ - ret = tda10071_rd_reg(priv, 0x50, &tmp); - if (ret) - goto error; + cmd.args[0] = CMD_BER_UPDATE_COUNTERS; + cmd.args[1] = 0; + cmd.args[2] = delivery_system; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; - if (tmp < 181) - tmp = 181; /* -75 dBm */ - else if (tmp > 236) - tmp = 236; /* -20 dBm */ + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len); + if (ret) + goto error; - /* scale value to 0x0000-0xffff */ - *strength = (tmp-181) * 0xffff / (236-181); + if (dev->delivery_system == SYS_DVBS) { + dev->dvbv3_ber = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + dev->post_bit_error += buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + dev->block_error += buf[4] << 8 | buf[5] << 0; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->block_error; + } else { + dev->dvbv3_ber = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += buf[0] << 8 | buf[1] << 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct tda10071_cmd cmd; - int ret, i, len; - u8 tmp, reg, buf[8]; - - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - *ber = priv->ber = 0; - ret = 0; - goto error; - } + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - switch (priv->delivery_system) { - case SYS_DVBS: - reg = 0x4c; - len = 8; - i = 1; - break; - case SYS_DVBS2: - reg = 0x4d; - len = 4; - i = 0; - break; - default: - *ber = priv->ber = 0; - return 0; - } + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + return 0; +} - ret = tda10071_rd_reg(priv, reg, &tmp); - if (ret) - goto error; +static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + unsigned int uitmp; - if (priv->meas_count[i] == tmp) { - dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__, - tmp); - *ber = priv->ber; - return 0; + if (c->strength.stat[0].scale == FE_SCALE_DECIBEL) { + uitmp = div_s64(c->strength.stat[0].svalue, 1000) + 256; + uitmp = clamp(uitmp, 181U, 236U); /* -75dBm - -20dBm */ + /* scale value to 0x0000-0xffff */ + *strength = (uitmp-181) * 0xffff / (236-181); } else { - priv->meas_count[i] = tmp; + *strength = 0; } + return 0; +} - cmd.args[0] = CMD_BER_UPDATE_COUNTERS; - cmd.args[1] = 0; - cmd.args[2] = i; - cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); - if (ret) - goto error; - - ret = tda10071_rd_regs(priv, cmd.len, buf, len); - if (ret) - goto error; - - if (priv->delivery_system == SYS_DVBS) { - *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - priv->ucb += (buf[4] << 8) | buf[5]; - } else { - *ber = (buf[0] << 8) | buf[1]; - } - priv->ber = *ber; +static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10071_dev *dev = fe->demodulator_priv; - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; + *ber = dev->dvbv3_ber; + return 0; } static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct tda10071_priv *priv = fe->demodulator_priv; - int ret = 0; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + if (c->block_error.stat[0].scale == FE_SCALE_COUNTER) + *ucblocks = c->block_error.stat[0].uvalue; + else *ucblocks = 0; - goto error; - } - - /* UCB is updated when BER is read. Assume BER is read anyway. */ - - *ucblocks = priv->ucb; - - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; + return 0; } static int tda10071_set_frontend(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 mode, rolloff, pilot, inversion, div; - fe_modulation_t modulation; + enum fe_modulation modulation; - dev_dbg(&priv->i2c->dev, - "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", - __func__, c->delivery_system, c->modulation, - c->frequency, c->symbol_rate, c->inversion, c->pilot, - c->rolloff); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); - priv->delivery_system = SYS_UNDEFINED; + dev->delivery_system = SYS_UNDEFINED; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -696,7 +591,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) inversion = 3; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__); + dev_dbg(&client->dev, "invalid inversion\n"); ret = -EINVAL; goto error; } @@ -722,8 +617,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) break; case ROLLOFF_AUTO: default: - dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", - __func__); + dev_dbg(&client->dev, "invalid rolloff\n"); ret = -EINVAL; goto error; } @@ -739,15 +633,13 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) pilot = 2; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", - __func__); + dev_dbg(&client->dev, "invalid pilot\n"); ret = -EINVAL; goto error; } break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto error; } @@ -757,15 +649,13 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) modulation == TDA10071_MODCOD[i].modulation && c->fec_inner == TDA10071_MODCOD[i].fec) { mode = TDA10071_MODCOD[i].val; - dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n", - __func__, mode); + dev_dbg(&client->dev, "mode found=%02x\n", mode); break; } } if (mode == 0xff) { - dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n", - __func__); + dev_dbg(&client->dev, "invalid parameter combination\n"); ret = -EINVAL; goto error; } @@ -775,11 +665,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) else div = 4; - ret = tda10071_wr_reg(priv, 0x81, div); + ret = regmap_write(dev->regmap, 0x81, div); if (ret) goto error; - ret = tda10071_wr_reg(priv, 0xe3, div); + ret = regmap_write(dev->regmap, 0xe3, div); if (ret) goto error; @@ -799,31 +689,32 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) cmd.args[13] = 0x00; cmd.args[14] = 0x00; cmd.len = 15; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - priv->delivery_system = c->delivery_system; + dev->delivery_system = c->delivery_system; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_get_frontend(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 buf[5], tmp; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - ret = -EFAULT; + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + ret = 0; goto error; } - ret = tda10071_rd_regs(priv, 0x30, buf, 5); + ret = regmap_bulk_read(dev->regmap, 0x30, buf, 5); if (ret) goto error; @@ -856,7 +747,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); - ret = tda10071_rd_regs(priv, 0x52, buf, 3); + ret = regmap_bulk_read(dev->regmap, 0x52, buf, 3); if (ret) goto error; @@ -864,15 +755,18 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_init(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct tda10071_cmd cmd; int ret, i, len, remaining, fw_size; + unsigned int uitmp; const struct firmware *fw; u8 *fw_file = TDA10071_FIRMWARE; u8 tmp, buf[4]; @@ -890,7 +784,7 @@ static int tda10071_init(struct dvb_frontend *fe) }; struct tda10071_reg_val_mask tab2[] = { { 0xf1, 0x70, 0xff }, - { 0x88, priv->cfg.pll_multiplier, 0x3f }, + { 0x88, dev->pll_multiplier, 0x3f }, { 0x89, 0x00, 0x10 }, { 0x89, 0x10, 0x10 }, { 0xc0, 0x01, 0x01 }, @@ -934,11 +828,11 @@ static int tda10071_init(struct dvb_frontend *fe) { 0xd5, 0x03, 0x03 }, }; - if (priv->warm) { + if (dev->warm) { /* warm state - wake up device from sleep */ for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = tda10071_wr_reg_mask(priv, tab[i].reg, + ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val, tab[i].mask); if (ret) goto error; @@ -948,78 +842,76 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[1] = 0; cmd.args[2] = 0; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; } else { /* cold state - try to download firmware */ /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + ret = request_firmware(&fw, fw_file, &client->dev); if (ret) { - dev_err(&priv->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_err(&client->dev, + "did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n", + fw_file, ret); goto error; } /* init */ for (i = 0; i < ARRAY_SIZE(tab2); i++) { - ret = tda10071_wr_reg_mask(priv, tab2[i].reg, + ret = tda10071_wr_reg_mask(dev, tab2[i].reg, tab2[i].val, tab2[i].mask); if (ret) goto error_release_firmware; } /* download firmware */ - ret = tda10071_wr_reg(priv, 0xe0, 0x7f); + ret = regmap_write(dev->regmap, 0xe0, 0x7f); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf7, 0x81); + ret = regmap_write(dev->regmap, 0xf7, 0x81); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf8, 0x00); + ret = regmap_write(dev->regmap, 0xf8, 0x00); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf9, 0x00); + ret = regmap_write(dev->regmap, 0xf9, 0x00); if (ret) goto error_release_firmware; - dev_info(&priv->i2c->dev, - "%s: found a '%s' in cold state, will try to load a firmware\n", - KBUILD_MODNAME, tda10071_ops.info.name); - dev_info(&priv->i2c->dev, - "%s: downloading firmware from file '%s'\n", - KBUILD_MODNAME, fw_file); + dev_info(&client->dev, + "found a '%s' in cold state, will try to load a firmware\n", + tda10071_ops.info.name); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_file); /* do not download last byte */ fw_size = fw->size - 1; for (remaining = fw_size; remaining > 0; - remaining -= (priv->cfg.i2c_wr_max - 1)) { + remaining -= (dev->i2c_wr_max - 1)) { len = remaining; - if (len > (priv->cfg.i2c_wr_max - 1)) - len = (priv->cfg.i2c_wr_max - 1); + if (len > (dev->i2c_wr_max - 1)) + len = (dev->i2c_wr_max - 1); - ret = tda10071_wr_regs(priv, 0xfa, + ret = regmap_bulk_write(dev->regmap, 0xfa, (u8 *) &fw->data[fw_size - remaining], len); if (ret) { - dev_err(&priv->i2c->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); + dev_err(&client->dev, + "firmware download failed=%d\n", ret); goto error_release_firmware; } } release_firmware(fw); - ret = tda10071_wr_reg(priv, 0xf7, 0x0c); + ret = regmap_write(dev->regmap, 0xf7, 0x0c); if (ret) goto error; - ret = tda10071_wr_reg(priv, 0xe0, 0x00); + ret = regmap_write(dev->regmap, 0xe0, 0x00); if (ret) goto error; @@ -1027,53 +919,52 @@ static int tda10071_init(struct dvb_frontend *fe) msleep(250); /* firmware status */ - ret = tda10071_rd_reg(priv, 0x51, &tmp); + ret = regmap_read(dev->regmap, 0x51, &uitmp); if (ret) goto error; - if (tmp) { - dev_info(&priv->i2c->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); + if (uitmp) { + dev_info(&client->dev, "firmware did not run\n"); ret = -EFAULT; goto error; } else { - priv->warm = true; + dev->warm = true; } cmd.args[0] = CMD_GET_FW_VERSION; cmd.len = 1; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_rd_regs(priv, cmd.len, buf, 4); + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, 4); if (ret) goto error; - dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n", - KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]); - dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", - KBUILD_MODNAME, tda10071_ops.info.name); + dev_info(&client->dev, "firmware version %d.%d.%d.%d\n", + buf[0], buf[1], buf[2], buf[3]); + dev_info(&client->dev, "found a '%s' in warm state\n", + tda10071_ops.info.name); - ret = tda10071_rd_regs(priv, 0x81, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x81, buf, 2); if (ret) goto error; cmd.args[0] = CMD_DEMOD_INIT; - cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; - cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; + cmd.args[1] = ((dev->clk / 1000) >> 8) & 0xff; + cmd.args[2] = ((dev->clk / 1000) >> 0) & 0xff; cmd.args[3] = buf[0]; cmd.args[4] = buf[1]; - cmd.args[5] = priv->cfg.pll_multiplier; - cmd.args[6] = priv->cfg.spec_inv; + cmd.args[5] = dev->pll_multiplier; + cmd.args[6] = dev->spec_inv; cmd.args[7] = 0x00; cmd.len = 8; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - if (priv->cfg.tuner_i2c_addr) - tmp = priv->cfg.tuner_i2c_addr; + if (dev->tuner_i2c_addr) + tmp = dev->tuner_i2c_addr; else tmp = 0x14; @@ -1093,22 +984,22 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[13] = 0x00; cmd.args[14] = 0x00; cmd.len = 15; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; cmd.args[0] = CMD_MPEG_CONFIG; cmd.args[1] = 0; - cmd.args[2] = priv->cfg.ts_mode; + cmd.args[2] = dev->ts_mode; cmd.args[3] = 0x00; cmd.args[4] = 0x04; cmd.args[5] = 0x00; cmd.len = 6; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01); + ret = regmap_update_bits(dev->regmap, 0xf0, 0x01, 0x01); if (ret) goto error; @@ -1124,7 +1015,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[9] = 30; cmd.args[10] = 30; cmd.len = 11; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -1133,22 +1024,33 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 14; cmd.args[3] = 14; cmd.len = 4; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; } + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return ret; error_release_firmware: release_firmware(fw); error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_sleep(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; struct tda10071_reg_val_mask tab[] = { @@ -1164,7 +1066,7 @@ static int tda10071_sleep(struct dvb_frontend *fe) { 0xce, 0x10, 0x10 }, }; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -1173,12 +1075,12 @@ static int tda10071_sleep(struct dvb_frontend *fe) cmd.args[1] = 0; cmd.args[2] = 1; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val, + ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val, tab[i].mask); if (ret) goto error; @@ -1186,7 +1088,7 @@ static int tda10071_sleep(struct dvb_frontend *fe) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -1200,71 +1102,6 @@ static int tda10071_get_tune_settings(struct dvb_frontend *fe, return 0; } -static void tda10071_release(struct dvb_frontend *fe) -{ - struct tda10071_priv *priv = fe->demodulator_priv; - kfree(priv); -} - -struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, - struct i2c_adapter *i2c) -{ - int ret; - struct tda10071_priv *priv = NULL; - u8 tmp; - - /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL); - if (priv == NULL) { - ret = -ENOMEM; - goto error; - } - - /* make sure demod i2c address is specified */ - if (!config->demod_i2c_addr) { - dev_dbg(&i2c->dev, "%s: invalid demod i2c address\n", __func__); - ret = -EINVAL; - goto error; - } - - /* make sure tuner i2c address is specified */ - if (!config->tuner_i2c_addr) { - dev_dbg(&i2c->dev, "%s: invalid tuner i2c address\n", __func__); - ret = -EINVAL; - goto error; - } - - /* setup the priv */ - priv->i2c = i2c; - memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); - - /* chip ID */ - ret = tda10071_rd_reg(priv, 0xff, &tmp); - if (ret || tmp != 0x0f) - goto error; - - /* chip type */ - ret = tda10071_rd_reg(priv, 0xdd, &tmp); - if (ret || tmp != 0x00) - goto error; - - /* chip version */ - ret = tda10071_rd_reg(priv, 0xfe, &tmp); - if (ret || tmp != 0x01) - goto error; - - /* create dvb_frontend */ - memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; - - return &priv->fe; -error: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; -} -EXPORT_SYMBOL(tda10071_attach); - static struct dvb_frontend_ops tda10071_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { @@ -1289,8 +1126,6 @@ static struct dvb_frontend_ops tda10071_ops = { FE_CAN_2G_MODULATION }, - .release = tda10071_release, - .get_tune_settings = tda10071_get_tune_settings, .init = tda10071_init, @@ -1313,6 +1148,119 @@ static struct dvb_frontend_ops tda10071_ops = { .set_voltage = tda10071_set_voltage, }; +static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client) +{ + struct tda10071_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static int tda10071_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tda10071_dev *dev; + struct tda10071_platform_data *pdata = client->dev.platform_data; + int ret; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + dev->client = client; + mutex_init(&dev->cmd_execute_mutex); + dev->clk = pdata->clk; + dev->i2c_wr_max = pdata->i2c_wr_max; + dev->ts_mode = pdata->ts_mode; + dev->spec_inv = pdata->spec_inv; + dev->pll_multiplier = pdata->pll_multiplier; + dev->tuner_i2c_addr = pdata->tuner_i2c_addr; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + + /* chip ID */ + ret = regmap_read(dev->regmap, 0xff, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x0f) { + ret = -ENODEV; + goto err_kfree; + } + + /* chip type */ + ret = regmap_read(dev->regmap, 0xdd, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x00) { + ret = -ENODEV; + goto err_kfree; + } + + /* chip version */ + ret = regmap_read(dev->regmap, 0xfe, &uitmp); + if (ret) + goto err_kfree; + if (uitmp != 0x01) { + ret = -ENODEV; + goto err_kfree; + } + + /* create dvb_frontend */ + memcpy(&dev->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + i2c_set_clientdata(client, dev); + + /* setup callbacks */ + pdata->get_dvb_frontend = tda10071_get_dvb_frontend; + + dev_info(&client->dev, "NXP TDA10071 successfully identified\n"); + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tda10071_remove(struct i2c_client *client) +{ + struct tda10071_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(dev); + return 0; +} + +static const struct i2c_device_id tda10071_id_table[] = { + {"tda10071_cx24118", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tda10071_id_table); + +static struct i2c_driver tda10071_driver = { + .driver = { + .name = "tda10071", + .suppress_bind_attrs = true, + }, + .probe = tda10071_probe, + .remove = tda10071_remove, + .id_table = tda10071_id_table, +}; + +module_i2c_driver(tda10071_driver); + MODULE_AUTHOR("Antti Palosaari "); MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver"); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/dvb-frontends/tda10071.h b/kernel/drivers/media/dvb-frontends/tda10071.h index da89f4249..8f184026e 100644 --- a/kernel/drivers/media/dvb-frontends/tda10071.h +++ b/kernel/drivers/media/dvb-frontends/tda10071.h @@ -21,67 +21,35 @@ #ifndef TDA10071_H #define TDA10071_H -#include #include -struct tda10071_config { - /* Demodulator I2C address. - * Default: none, must set - * Values: 0x55, - */ - u8 demod_i2c_addr; +/* + * I2C address + * 0x05, 0x55, + */ - /* Tuner I2C address. - * Default: none, must set - * Values: 0x14, 0x54, ... - */ - u8 tuner_i2c_addr; +/** + * struct tda10071_platform_data - Platform data for the tda10071 driver + * @clk: Clock frequency. + * @i2c_wr_max: Max bytes I2C adapter can write at once. + * @ts_mode: TS mode. + * @spec_inv: Input spectrum inversion. + * @pll_multiplier: PLL multiplier. + * @tuner_i2c_addr: CX24118A tuner I2C address (0x14, 0x54, ...). + * @get_dvb_frontend: Get DVB frontend. + */ - /* Max bytes I2C provider can write at once. - * Note: Buffer is taken from the stack currently! - * Default: none, must set - * Values: - */ +struct tda10071_platform_data { + u32 clk; u16 i2c_wr_max; - - /* TS output mode. - * Default: TDA10071_TS_SERIAL - * Values: - */ #define TDA10071_TS_SERIAL 0 #define TDA10071_TS_PARALLEL 1 u8 ts_mode; - - /* Input spectrum inversion. - * Default: 0 - * Values: 0, 1 - */ bool spec_inv; - - /* Xtal frequency Hz - * Default: none, must set - * Values: - */ - u32 xtal; - - /* PLL multiplier. - * Default: none, must set - * Values: - */ u8 pll_multiplier; -}; - + u8 tuner_i2c_addr; -#if IS_REACHABLE(CONFIG_DVB_TDA10071) -extern struct dvb_frontend *tda10071_attach( - const struct tda10071_config *config, struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend *tda10071_attach( - const struct tda10071_config *config, struct i2c_adapter *i2c) -{ - dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +}; #endif /* TDA10071_H */ diff --git a/kernel/drivers/media/dvb-frontends/tda10071_priv.h b/kernel/drivers/media/dvb-frontends/tda10071_priv.h index 03f839c43..b9c360180 100644 --- a/kernel/drivers/media/dvb-frontends/tda10071_priv.h +++ b/kernel/drivers/media/dvb-frontends/tda10071_priv.h @@ -24,24 +24,33 @@ #include "dvb_frontend.h" #include "tda10071.h" #include +#include -struct tda10071_priv { - struct i2c_adapter *i2c; +struct tda10071_dev { struct dvb_frontend fe; - struct tda10071_config cfg; + struct i2c_client *client; + struct regmap *regmap; + struct mutex cmd_execute_mutex; + u32 clk; + u16 i2c_wr_max; + u8 ts_mode; + bool spec_inv; + u8 pll_multiplier; + u8 tuner_i2c_addr; - u8 meas_count[2]; - u32 ber; - u32 ucb; - fe_status_t fe_status; - fe_delivery_system_t delivery_system; + u8 meas_count; + u32 dvbv3_ber; + enum fe_status fe_status; + enum fe_delivery_system delivery_system; bool warm; /* FW running */ + u64 post_bit_error; + u64 block_error; }; static struct tda10071_modcod { - fe_delivery_system_t delivery_system; - fe_modulation_t modulation; - fe_code_rate_t fec; + enum fe_delivery_system delivery_system; + enum fe_modulation modulation; + enum fe_code_rate fec; u8 val; } TDA10071_MODCOD[] = { /* NBC-QPSK */ diff --git a/kernel/drivers/media/dvb-frontends/tda10086.c b/kernel/drivers/media/dvb-frontends/tda10086.c index fcfe2e080..95a33e187 100644 --- a/kernel/drivers/media/dvb-frontends/tda10086.c +++ b/kernel/drivers/media/dvb-frontends/tda10086.c @@ -185,7 +185,8 @@ static void tda10086_diseqc_wait(struct tda10086_state *state) } } -static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int tda10086_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct tda10086_state* state = fe->demodulator_priv; u8 t22k_off = 0x80; @@ -238,7 +239,8 @@ static int tda10086_send_master_cmd (struct dvb_frontend* fe, return 0; } -static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int tda10086_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct tda10086_state* state = fe->demodulator_priv; u8 oldval = tda10086_read_byte(state, 0x36); @@ -472,8 +474,8 @@ static int tda10086_get_frontend(struct dvb_frontend *fe) return -EINVAL; /* calculate the updated frequency (note: we convert from Hz->kHz) */ - tmp64 = tda10086_read_byte(state, 0x52); - tmp64 |= (tda10086_read_byte(state, 0x51) << 8); + tmp64 = ((u64)tda10086_read_byte(state, 0x52) + | (tda10086_read_byte(state, 0x51) << 8)); if (tmp64 & 0x8000) tmp64 |= 0xffffffffffff0000ULL; tmp64 = (tmp64 * (SACLK/1000ULL)); @@ -551,7 +553,8 @@ static int tda10086_get_frontend(struct dvb_frontend *fe) return 0; } -static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status) +static int tda10086_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) { struct tda10086_state* state = fe->demodulator_priv; u8 val; diff --git a/kernel/drivers/media/dvb-frontends/tda8083.c b/kernel/drivers/media/dvb-frontends/tda8083.c index 69e62f42e..796543fa2 100644 --- a/kernel/drivers/media/dvb-frontends/tda8083.c +++ b/kernel/drivers/media/dvb-frontends/tda8083.c @@ -97,7 +97,8 @@ static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) return val; } -static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion) +static int tda8083_set_inversion(struct tda8083_state *state, + enum fe_spectral_inversion inversion) { /* XXX FIXME: implement other modes than FEC_AUTO */ if (inversion == INVERSION_AUTO) @@ -106,7 +107,7 @@ static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inver return -EINVAL; } -static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) +static int tda8083_set_fec(struct tda8083_state *state, enum fe_code_rate fec) { if (fec == FEC_AUTO) return tda8083_writereg (state, 0x07, 0xff); @@ -117,11 +118,13 @@ static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) return -EINVAL; } -static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state) +static enum fe_code_rate tda8083_get_fec(struct tda8083_state *state) { u8 index; - static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, - FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; + static enum fe_code_rate fec_tab[] = { + FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 + }; index = tda8083_readreg(state, 0x0e) & 0x07; @@ -178,7 +181,8 @@ static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) } } -static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) +static int tda8083_set_tone(struct tda8083_state *state, + enum fe_sec_tone_mode tone) { tda8083_writereg (state, 0x26, 0xf1); @@ -192,7 +196,8 @@ static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t ton } } -static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage) +static int tda8083_set_voltage(struct tda8083_state *state, + enum fe_sec_voltage voltage) { switch (voltage) { case SEC_VOLTAGE_13: @@ -204,7 +209,8 @@ static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t vo } } -static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst) +static int tda8083_send_diseqc_burst(struct tda8083_state *state, + enum fe_sec_mini_cmd burst) { switch (burst) { case SEC_MINI_A: @@ -222,8 +228,8 @@ static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_c return 0; } -static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd *m) +static int tda8083_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) { struct tda8083_state* state = fe->demodulator_priv; int i; @@ -240,7 +246,8 @@ static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, return 0; } -static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int tda8083_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct tda8083_state* state = fe->demodulator_priv; @@ -372,7 +379,8 @@ static int tda8083_init(struct dvb_frontend* fe) return 0; } -static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +static int tda8083_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct tda8083_state* state = fe->demodulator_priv; @@ -383,7 +391,8 @@ static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t return 0; } -static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int tda8083_diseqc_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct tda8083_state* state = fe->demodulator_priv; @@ -394,7 +403,8 @@ static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t t return 0; } -static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int tda8083_diseqc_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct tda8083_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/ts2020.c b/kernel/drivers/media/dvb-frontends/ts2020.c index 90164a38c..7979e5d64 100644 --- a/kernel/drivers/media/dvb-frontends/ts2020.c +++ b/kernel/drivers/media/dvb-frontends/ts2020.c @@ -21,23 +21,32 @@ #include "dvb_frontend.h" #include "ts2020.h" +#include +#include #define TS2020_XTAL_FREQ 27000 /* in kHz */ #define FREQ_OFFSET_LOW_SYM_RATE 3000 struct ts2020_priv { + struct i2c_client *client; + struct mutex regmap_mutex; + struct regmap_config regmap_config; + struct regmap *regmap; struct dvb_frontend *fe; + struct delayed_work stat_work; + int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm); /* i2c details */ - int i2c_address; struct i2c_adapter *i2c; + int i2c_address; + bool loop_through:1; u8 clk_out:2; u8 clk_out_div:5; - u32 frequency; - u32 frequency_div; + bool dont_poll:1; + u32 frequency_div; /* LO output divider switch frequency */ + u32 frequency_khz; /* actual used LO frequency */ #define TS2020_M88TS2020 0 #define TS2020_M88TS2022 1 u8 tuner; - u8 loop_through:1; }; struct ts2020_reg_val { @@ -45,84 +54,23 @@ struct ts2020_reg_val { u8 val; }; -static int ts2020_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} +static void ts2020_stat_work(struct work_struct *work); -static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +static int ts2020_release(struct dvb_frontend *fe) { struct ts2020_priv *priv = fe->tuner_priv; - u8 buf[] = { reg, data }; - struct i2c_msg msg[] = { - { - .addr = priv->i2c_address, - .flags = 0, - .buf = buf, - .len = 2 - } - }; - int err; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - err = i2c_transfer(priv->i2c, msg, 1); - if (err != 1) { - printk(KERN_ERR - "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", - __func__, err, reg, data); - return -EREMOTEIO; - } + struct i2c_client *client = priv->client; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + dev_dbg(&client->dev, "\n"); + i2c_unregister_device(client); return 0; } -static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) -{ - struct ts2020_priv *priv = fe->tuner_priv; - int ret; - u8 b0[] = { reg }; - u8 b1[] = { 0 }; - struct i2c_msg msg[] = { - { - .addr = priv->i2c_address, - .flags = 0, - .buf = b0, - .len = 1 - }, { - .addr = priv->i2c_address, - .flags = I2C_M_RD, - .buf = b1, - .len = 1 - } - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = i2c_transfer(priv->i2c, msg, 2); - - if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", - __func__, reg, ret); - return ret; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return b1[0]; -} - static int ts2020_sleep(struct dvb_frontend *fe) { struct ts2020_priv *priv = fe->tuner_priv; + int ret; u8 u8tmp; if (priv->tuner == TS2020_M88TS2020) @@ -130,24 +78,32 @@ static int ts2020_sleep(struct dvb_frontend *fe) else u8tmp = 0x00; - return ts2020_writereg(fe, u8tmp, 0x00); + ret = regmap_write(priv->regmap, u8tmp, 0x00); + if (ret < 0) + return ret; + + /* stop statistics polling */ + if (!priv->dont_poll) + cancel_delayed_work_sync(&priv->stat_work); + return 0; } static int ts2020_init(struct dvb_frontend *fe) { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct ts2020_priv *priv = fe->tuner_priv; int i; u8 u8tmp; if (priv->tuner == TS2020_M88TS2020) { - ts2020_writereg(fe, 0x42, 0x73); - ts2020_writereg(fe, 0x05, priv->clk_out_div); - ts2020_writereg(fe, 0x20, 0x27); - ts2020_writereg(fe, 0x07, 0x02); - ts2020_writereg(fe, 0x11, 0xff); - ts2020_writereg(fe, 0x60, 0xf9); - ts2020_writereg(fe, 0x08, 0x01); - ts2020_writereg(fe, 0x00, 0x41); + regmap_write(priv->regmap, 0x42, 0x73); + regmap_write(priv->regmap, 0x05, priv->clk_out_div); + regmap_write(priv->regmap, 0x20, 0x27); + regmap_write(priv->regmap, 0x07, 0x02); + regmap_write(priv->regmap, 0x11, 0xff); + regmap_write(priv->regmap, 0x60, 0xf9); + regmap_write(priv->regmap, 0x08, 0x01); + regmap_write(priv->regmap, 0x00, 0x41); } else { static const struct ts2020_reg_val reg_vals[] = { {0x7d, 0x9d}, @@ -163,8 +119,8 @@ static int ts2020_init(struct dvb_frontend *fe) {0x12, 0xa0}, }; - ts2020_writereg(fe, 0x00, 0x01); - ts2020_writereg(fe, 0x00, 0x03); + regmap_write(priv->regmap, 0x00, 0x01); + regmap_write(priv->regmap, 0x00, 0x03); switch (priv->clk_out) { case TS2020_CLK_OUT_DISABLED: @@ -172,7 +128,7 @@ static int ts2020_init(struct dvb_frontend *fe) break; case TS2020_CLK_OUT_ENABLED: u8tmp = 0x70; - ts2020_writereg(fe, 0x05, priv->clk_out_div); + regmap_write(priv->regmap, 0x05, priv->clk_out_div); break; case TS2020_CLK_OUT_ENABLED_XTALOUT: u8tmp = 0x6c; @@ -182,50 +138,61 @@ static int ts2020_init(struct dvb_frontend *fe) break; } - ts2020_writereg(fe, 0x42, u8tmp); + regmap_write(priv->regmap, 0x42, u8tmp); if (priv->loop_through) u8tmp = 0xec; else u8tmp = 0x6c; - ts2020_writereg(fe, 0x62, u8tmp); + regmap_write(priv->regmap, 0x62, u8tmp); for (i = 0; i < ARRAY_SIZE(reg_vals); i++) - ts2020_writereg(fe, reg_vals[i].reg, reg_vals[i].val); + regmap_write(priv->regmap, reg_vals[i].reg, + reg_vals[i].val); } + /* Initialise v5 stats here */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].uvalue = 0; + + /* Start statistics polling by invoking the work function */ + ts2020_stat_work(&priv->stat_work.work); return 0; } static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) { + struct ts2020_priv *priv = fe->tuner_priv; int ret; - ret = ts2020_writereg(fe, 0x51, 0x1f - offset); - ret |= ts2020_writereg(fe, 0x51, 0x1f); - ret |= ts2020_writereg(fe, 0x50, offset); - ret |= ts2020_writereg(fe, 0x50, 0x00); + ret = regmap_write(priv->regmap, 0x51, 0x1f - offset); + ret |= regmap_write(priv->regmap, 0x51, 0x1f); + ret |= regmap_write(priv->regmap, 0x50, offset); + ret |= regmap_write(priv->regmap, 0x50, 0x00); msleep(20); return ret; } static int ts2020_set_tuner_rf(struct dvb_frontend *fe) { - int reg; - - reg = ts2020_readreg(fe, 0x3d); - reg &= 0x7f; - if (reg < 0x16) - reg = 0xa1; - else if (reg == 0x16) - reg = 0x99; + struct ts2020_priv *dev = fe->tuner_priv; + int ret; + unsigned int utmp; + + ret = regmap_read(dev->regmap, 0x3d, &utmp); + utmp &= 0x7f; + if (utmp < 0x16) + utmp = 0xa1; + else if (utmp == 0x16) + utmp = 0x99; else - reg = 0xf9; + utmp = 0xf9; - ts2020_writereg(fe, 0x60, reg); - reg = ts2020_tuner_gate_ctrl(fe, 0x08); + regmap_write(dev->regmap, 0x60, utmp); + ret = ts2020_tuner_gate_ctrl(fe, 0x08); - return reg; + return ret; } static int ts2020_set_params(struct dvb_frontend *fe) @@ -233,45 +200,62 @@ static int ts2020_set_params(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct ts2020_priv *priv = fe->tuner_priv; int ret; - u32 frequency = c->frequency; - s32 offset_khz; - u32 symbol_rate = (c->symbol_rate / 1000); + unsigned int utmp; u32 f3db, gdiv28; - u16 value, ndiv, lpf_coeff; - u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; - u8 lo = 0x01, div4 = 0x0; - - /* Calculate frequency divider */ - if (frequency < priv->frequency_div) { - lo |= 0x10; - div4 = 0x1; - ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ; - } else - ndiv = (frequency * 14 * 2) / TS2020_XTAL_FREQ; - ndiv = ndiv + ndiv % 2; - ndiv = ndiv - 1024; + u16 u16tmp, value, lpf_coeff; + u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n; + unsigned int frequency_khz = c->frequency; + + /* + * Integer-N PLL synthesizer + * kHz is used for all calculations to keep calculations within 32-bit + */ + f_ref_khz = TS2020_XTAL_FREQ; + div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); + + /* select LO output divider */ + if (frequency_khz < priv->frequency_div) { + div_out = 4; + reg10 = 0x10; + } else { + div_out = 2; + reg10 = 0x00; + } + + f_vco_khz = frequency_khz * div_out; + pll_n = f_vco_khz * div_ref / f_ref_khz; + pll_n += pll_n % 2; + priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; + + pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", + priv->frequency_khz, priv->frequency_khz - c->frequency, + f_vco_khz, pll_n, div_ref, div_out); if (priv->tuner == TS2020_M88TS2020) { lpf_coeff = 2766; - ret = ts2020_writereg(fe, 0x10, 0x80 | lo); + reg10 |= 0x01; + ret = regmap_write(priv->regmap, 0x10, reg10); } else { lpf_coeff = 3200; - ret = ts2020_writereg(fe, 0x10, 0x0b); - ret |= ts2020_writereg(fe, 0x11, 0x40); + reg10 |= 0x0b; + ret = regmap_write(priv->regmap, 0x10, reg10); + ret |= regmap_write(priv->regmap, 0x11, 0x40); } - /* Set frequency divider */ - ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); - ret |= ts2020_writereg(fe, 0x02, ndiv & 0xff); + u16tmp = pll_n - 1024; + buf[0] = (u16tmp >> 8) & 0xff; + buf[1] = (u16tmp >> 0) & 0xff; + buf[2] = div_ref - 8; + + ret |= regmap_write(priv->regmap, 0x01, buf[0]); + ret |= regmap_write(priv->regmap, 0x02, buf[1]); + ret |= regmap_write(priv->regmap, 0x03, buf[2]); - ret |= ts2020_writereg(fe, 0x03, 0x06); ret |= ts2020_tuner_gate_ctrl(fe, 0x10); if (ret < 0) return -ENODEV; - /* Tuner Frequency Range */ - ret = ts2020_writereg(fe, 0x10, lo); - ret |= ts2020_tuner_gate_ctrl(fe, 0x08); /* Tuner RF */ @@ -279,28 +263,26 @@ static int ts2020_set_params(struct dvb_frontend *fe) ret |= ts2020_set_tuner_rf(fe); gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; - ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); + ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff); ret |= ts2020_tuner_gate_ctrl(fe, 0x04); if (ret < 0) return -ENODEV; if (priv->tuner == TS2020_M88TS2022) { - ret = ts2020_writereg(fe, 0x25, 0x00); - ret |= ts2020_writereg(fe, 0x27, 0x70); - ret |= ts2020_writereg(fe, 0x41, 0x09); - ret |= ts2020_writereg(fe, 0x08, 0x0b); + ret = regmap_write(priv->regmap, 0x25, 0x00); + ret |= regmap_write(priv->regmap, 0x27, 0x70); + ret |= regmap_write(priv->regmap, 0x41, 0x09); + ret |= regmap_write(priv->regmap, 0x08, 0x0b); if (ret < 0) return -ENODEV; } - value = ts2020_readreg(fe, 0x26); + regmap_read(priv->regmap, 0x26, &utmp); + value = utmp; - f3db = (symbol_rate * 135) / 200 + 2000; - f3db += FREQ_OFFSET_LOW_SYM_RATE; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; + f3db = (c->bandwidth_hz / 1000 / 2) + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */ + f3db = clamp(f3db, 7000U, 40000U); gdiv28 = gdiv28 * 207 / (value * 2 + 151); mlpf_max = gdiv28 * 135 / 100; @@ -327,19 +309,14 @@ static int ts2020_set_params(struct dvb_frontend *fe) if (lpf_mxdiv > mlpf_max) lpf_mxdiv = mlpf_max; - ret = ts2020_writereg(fe, 0x04, lpf_mxdiv); - ret |= ts2020_writereg(fe, 0x06, nlpf); + ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv); + ret |= regmap_write(priv->regmap, 0x06, nlpf); ret |= ts2020_tuner_gate_ctrl(fe, 0x04); ret |= ts2020_tuner_gate_ctrl(fe, 0x01); msleep(80); - /* calculate offset assuming 96000kHz*/ - offset_khz = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2; - - priv->frequency = offset_khz; return (ret < 0) ? -EINVAL : 0; } @@ -347,8 +324,8 @@ static int ts2020_set_params(struct dvb_frontend *fe) static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct ts2020_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; + *frequency = priv->frequency_khz; return 0; } @@ -358,28 +335,164 @@ static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -/* read TS2020 signal strength */ -static int ts2020_read_signal_strength(struct dvb_frontend *fe, - u16 *signal_strength) +/* + * Get the tuner gain. + * @fe: The front end for which we're determining the gain + * @v_agc: The voltage of the AGC from the demodulator (0-2600mV) + * @_gain: Where to store the gain (in 0.001dB units) + * + * Returns 0 or a negative error code. + */ +static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc, + __s64 *_gain) +{ + struct ts2020_priv *priv = fe->tuner_priv; + unsigned long gain1, gain2, gain3; + unsigned utmp; + int ret; + + /* Read the RF gain */ + ret = regmap_read(priv->regmap, 0x3d, &utmp); + if (ret < 0) + return ret; + gain1 = utmp & 0x1f; + + /* Read the baseband gain */ + ret = regmap_read(priv->regmap, 0x21, &utmp); + if (ret < 0) + return ret; + gain2 = utmp & 0x1f; + + switch (priv->tuner) { + case TS2020_M88TS2020: + gain1 = clamp_t(long, gain1, 0, 15); + gain2 = clamp_t(long, gain2, 0, 13); + v_agc = clamp_t(long, v_agc, 400, 1100); + + *_gain = -(gain1 * 2330 + + gain2 * 3500 + + v_agc * 24 / 10 * 10 + + 10000); + /* gain in range -19600 to -116850 in units of 0.001dB */ + break; + + case TS2020_M88TS2022: + ret = regmap_read(priv->regmap, 0x66, &utmp); + if (ret < 0) + return ret; + gain3 = (utmp >> 3) & 0x07; + + gain1 = clamp_t(long, gain1, 0, 15); + gain2 = clamp_t(long, gain2, 2, 16); + gain3 = clamp_t(long, gain3, 0, 6); + v_agc = clamp_t(long, v_agc, 600, 1600); + + *_gain = -(gain1 * 2650 + + gain2 * 3380 + + gain3 * 2850 + + v_agc * 176 / 100 * 10 - + 30000); + /* gain in range -47320 to -158950 in units of 0.001dB */ + break; + } + + return 0; +} + +/* + * Get the AGC information from the demodulator and use that to calculate the + * tuner gain. + */ +static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain) { - u16 sig_reading, sig_strength; - u8 rfgain, bbgain; + struct ts2020_priv *priv = fe->tuner_priv; + int v_agc = 0, ret; + u8 agc_pwm; - rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; - bbgain = ts2020_readreg(fe, 0x21) & 0x1f; + /* Read the AGC PWM rate from the demodulator */ + if (priv->get_agc_pwm) { + ret = priv->get_agc_pwm(fe, &agc_pwm); + if (ret < 0) + return ret; - if (rfgain > 15) - rfgain = 15; - if (bbgain > 13) - bbgain = 13; + switch (priv->tuner) { + case TS2020_M88TS2020: + v_agc = (int)agc_pwm * 20 - 1166; + break; + case TS2020_M88TS2022: + v_agc = (int)agc_pwm * 16 - 670; + break; + } - sig_reading = rfgain * 2 + bbgain * 3; + if (v_agc < 0) + v_agc = 0; + } - sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + return ts2020_read_tuner_gain(fe, v_agc, _gain); +} - /* cook the value to be suitable for szap-s2 human readable output */ - *signal_strength = sig_strength * 1000; +/* + * Gather statistics on a regular basis + */ +static void ts2020_stat_work(struct work_struct *work) +{ + struct ts2020_priv *priv = container_of(work, struct ts2020_priv, + stat_work.work); + struct i2c_client *client = priv->client; + struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache; + int ret; + + dev_dbg(&client->dev, "\n"); + + ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue); + if (ret < 0) + goto err; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + + if (!priv->dont_poll) + schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000)); + return; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); +} + +/* + * Read TS2020 signal strength in v3 format. + */ +static int ts2020_read_signal_strength(struct dvb_frontend *fe, + u16 *_signal_strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->tuner_priv; + unsigned strength; + __s64 gain; + + if (priv->dont_poll) + ts2020_stat_work(&priv->stat_work.work); + + if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { + *_signal_strength = 0; + return 0; + } + + gain = c->strength.stat[0].svalue; + + /* Calculate the signal strength based on the total gain of the tuner */ + if (gain < -85000) + /* 0%: no signal or weak signal */ + strength = 0; + else if (gain < -65000) + /* 0% - 60%: weak signal */ + strength = 0 + div64_s64((85000 + gain) * 3, 1000); + else if (gain < -45000) + /* 60% - 90%: normal signal */ + strength = 60 + div64_s64((65000 + gain) * 3, 2000); + else + /* 90% - 99%: strong signal */ + strength = 90 + div64_s64((45000 + gain), 5000); + + *_signal_strength = strength * 65535 / 100; return 0; } @@ -402,53 +515,50 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, const struct ts2020_config *config, struct i2c_adapter *i2c) { - struct ts2020_priv *priv = NULL; - u8 buf; - - priv = kzalloc(sizeof(struct ts2020_priv), GFP_KERNEL); - if (priv == NULL) + struct i2c_client *client; + struct i2c_board_info board_info; + + /* This is only used by ts2020_probe() so can be on the stack */ + struct ts2020_config pdata; + + memcpy(&pdata, config, sizeof(pdata)); + pdata.fe = fe; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "ts2020", I2C_NAME_SIZE); + board_info.addr = config->tuner_address; + board_info.platform_data = &pdata; + client = i2c_new_device(i2c, &board_info); + if (!client || !client->dev.driver) return NULL; - priv->i2c_address = config->tuner_address; - priv->i2c = i2c; - priv->clk_out = config->clk_out; - priv->clk_out_div = config->clk_out_div; - priv->frequency_div = config->frequency_div; - priv->fe = fe; - fe->tuner_priv = priv; - - if (!priv->frequency_div) - priv->frequency_div = 1060000; - - /* Wake Up the tuner */ - if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) { - ts2020_writereg(fe, 0x00, 0x01); - msleep(2); - } + return fe; +} +EXPORT_SYMBOL(ts2020_attach); - ts2020_writereg(fe, 0x00, 0x03); - msleep(2); - - /* Check the tuner version */ - buf = ts2020_readreg(fe, 0x00); - if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) { - printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); - priv->tuner = TS2020_M88TS2020; - } else if ((buf == 0x83) || (buf == 0xc3)) { - printk(KERN_INFO "%s: Find tuner TS2022!\n", __func__); - priv->tuner = TS2020_M88TS2022; - } else { - printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); - kfree(priv); - return NULL; - } +/* + * We implement own regmap locking due to legacy DVB attach which uses frontend + * gate control callback to control I2C bus access. We can open / close gate and + * serialize whole open / I2C-operation / close sequence at the same. + */ +static void ts2020_regmap_lock(void *__dev) +{ + struct ts2020_priv *dev = __dev; - memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, - sizeof(struct dvb_tuner_ops)); + mutex_lock(&dev->regmap_mutex); + if (dev->fe->ops.i2c_gate_ctrl) + dev->fe->ops.i2c_gate_ctrl(dev->fe, 1); +} - return fe; +static void ts2020_regmap_unlock(void *__dev) +{ + struct ts2020_priv *dev = __dev; + + if (dev->fe->ops.i2c_gate_ctrl) + dev->fe->ops.i2c_gate_ctrl(dev->fe, 0); + mutex_unlock(&dev->regmap_mutex); } -EXPORT_SYMBOL(ts2020_attach); static int ts2020_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -467,38 +577,54 @@ static int ts2020_probe(struct i2c_client *client, goto err; } + /* create regmap */ + mutex_init(&dev->regmap_mutex); + dev->regmap_config.reg_bits = 8, + dev->regmap_config.val_bits = 8, + dev->regmap_config.lock = ts2020_regmap_lock, + dev->regmap_config.unlock = ts2020_regmap_unlock, + dev->regmap_config.lock_arg = dev, + dev->regmap = regmap_init_i2c(client, &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } + dev->i2c = client->adapter; dev->i2c_address = client->addr; + dev->loop_through = pdata->loop_through; dev->clk_out = pdata->clk_out; dev->clk_out_div = pdata->clk_out_div; + dev->dont_poll = pdata->dont_poll; dev->frequency_div = pdata->frequency_div; dev->fe = fe; + dev->get_agc_pwm = pdata->get_agc_pwm; fe->tuner_priv = dev; + dev->client = client; + INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work); /* check if the tuner is there */ - ret = ts2020_readreg(fe, 0x00); - if (ret < 0) - goto err; - utmp = ret; + ret = regmap_read(dev->regmap, 0x00, &utmp); + if (ret) + goto err_regmap_exit; if ((utmp & 0x03) == 0x00) { - ret = ts2020_writereg(fe, 0x00, 0x01); + ret = regmap_write(dev->regmap, 0x00, 0x01); if (ret) - goto err; + goto err_regmap_exit; usleep_range(2000, 50000); } - ret = ts2020_writereg(fe, 0x00, 0x03); + ret = regmap_write(dev->regmap, 0x00, 0x03); if (ret) - goto err; + goto err_regmap_exit; usleep_range(2000, 50000); - ret = ts2020_readreg(fe, 0x00); - if (ret < 0) - goto err; - utmp = ret; + ret = regmap_read(dev->regmap, 0x00, &utmp); + if (ret) + goto err_regmap_exit; dev_dbg(&client->dev, "chip_id=%02x\n", utmp); @@ -520,7 +646,7 @@ static int ts2020_probe(struct i2c_client *client, break; default: ret = -ENODEV; - goto err; + goto err_regmap_exit; } if (dev->tuner == TS2020_M88TS2022) { @@ -530,63 +656,64 @@ static int ts2020_probe(struct i2c_client *client, break; case TS2020_CLK_OUT_ENABLED: u8tmp = 0x70; - ret = ts2020_writereg(fe, 0x05, dev->clk_out_div); + ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div); if (ret) - goto err; + goto err_regmap_exit; break; case TS2020_CLK_OUT_ENABLED_XTALOUT: u8tmp = 0x6c; break; default: ret = -EINVAL; - goto err; + goto err_regmap_exit; } - ret = ts2020_writereg(fe, 0x42, u8tmp); + ret = regmap_write(dev->regmap, 0x42, u8tmp); if (ret) - goto err; + goto err_regmap_exit; if (dev->loop_through) u8tmp = 0xec; else u8tmp = 0x6c; - ret = ts2020_writereg(fe, 0x62, u8tmp); + ret = regmap_write(dev->regmap, 0x62, u8tmp); if (ret) - goto err; + goto err_regmap_exit; } /* sleep */ - ret = ts2020_writereg(fe, 0x00, 0x00); + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) - goto err; + goto err_regmap_exit; dev_info(&client->dev, "Montage Technology %s successfully identified\n", chip_str); memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->ops.tuner_ops.release = NULL; + if (!pdata->attach_in_use) + fe->ops.tuner_ops.release = NULL; i2c_set_clientdata(client, dev); return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); err: dev_dbg(&client->dev, "failed=%d\n", ret); - kfree(dev); return ret; } static int ts2020_remove(struct i2c_client *client) { struct ts2020_priv *dev = i2c_get_clientdata(client); - struct dvb_frontend *fe = dev->fe; dev_dbg(&client->dev, "\n"); - memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = NULL; + regmap_exit(dev->regmap); kfree(dev); - return 0; } @@ -599,7 +726,6 @@ MODULE_DEVICE_TABLE(i2c, ts2020_id_table); static struct i2c_driver ts2020_driver = { .driver = { - .owner = THIS_MODULE, .name = "ts2020", }, .probe = ts2020_probe, diff --git a/kernel/drivers/media/dvb-frontends/ts2020.h b/kernel/drivers/media/dvb-frontends/ts2020.h index 1714af94e..9220e5cf0 100644 --- a/kernel/drivers/media/dvb-frontends/ts2020.h +++ b/kernel/drivers/media/dvb-frontends/ts2020.h @@ -32,7 +32,7 @@ struct ts2020_config { /* * RF loop-through */ - u8 loop_through:1; + bool loop_through:1; /* * clock output @@ -48,14 +48,27 @@ struct ts2020_config { */ u8 clk_out_div:5; + /* Set to true to suppress stat polling */ + bool dont_poll:1; + /* * pointer to DVB frontend */ struct dvb_frontend *fe; + + /* + * driver private, do not set value + */ + u8 attach_in_use:1; + + /* Operation to be called by the ts2020 driver to get the value of the + * AGC PWM tuner input as theoretically output by the demodulator. + */ + int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm); }; +/* Do not add new ts2020_attach() users! Use I2C bindings instead. */ #if IS_REACHABLE(CONFIG_DVB_TS2020) - extern struct dvb_frontend *ts2020_attach( struct dvb_frontend *fe, const struct ts2020_config *config, diff --git a/kernel/drivers/media/dvb-frontends/ves1820.c b/kernel/drivers/media/dvb-frontends/ves1820.c index bb42b563c..aacfdda3e 100644 --- a/kernel/drivers/media/dvb-frontends/ves1820.c +++ b/kernel/drivers/media/dvb-frontends/ves1820.c @@ -90,7 +90,8 @@ static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) return b1[0]; } -static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion) +static int ves1820_setup_reg0(struct ves1820_state *state, + u8 reg0, enum fe_spectral_inversion inversion) { reg0 |= state->reg0 & 0x62; @@ -237,7 +238,8 @@ static int ves1820_set_parameters(struct dvb_frontend *fe) return 0; } -static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int ves1820_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct ves1820_state* state = fe->demodulator_priv; int sync; diff --git a/kernel/drivers/media/dvb-frontends/ves1x93.c b/kernel/drivers/media/dvb-frontends/ves1x93.c index 9c17eacae..526952396 100644 --- a/kernel/drivers/media/dvb-frontends/ves1x93.c +++ b/kernel/drivers/media/dvb-frontends/ves1x93.c @@ -41,7 +41,7 @@ struct ves1x93_state { struct dvb_frontend frontend; /* previous uncorrected block counter */ - fe_spectral_inversion_t inversion; + enum fe_spectral_inversion inversion; u8 *init_1x93_tab; u8 *init_1x93_wtab; u8 tab_size; @@ -130,7 +130,8 @@ static int ves1x93_clr_bit (struct ves1x93_state* state) return 0; } -static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion) +static int ves1x93_set_inversion(struct ves1x93_state *state, + enum fe_spectral_inversion inversion) { u8 val; @@ -156,7 +157,7 @@ static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inver return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); } -static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) +static int ves1x93_set_fec(struct ves1x93_state *state, enum fe_code_rate fec) { if (fec == FEC_AUTO) return ves1x93_writereg (state, 0x0d, 0x08); @@ -166,7 +167,7 @@ static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); } -static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state) +static enum fe_code_rate ves1x93_get_fec(struct ves1x93_state *state) { return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); } @@ -281,7 +282,8 @@ static int ves1x93_init (struct dvb_frontend* fe) return 0; } -static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int ves1x93_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct ves1x93_state* state = fe->demodulator_priv; @@ -297,7 +299,8 @@ static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag } } -static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int ves1x93_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct ves1x93_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/dvb-frontends/zl10353.c b/kernel/drivers/media/dvb-frontends/zl10353.c index 82946cd51..ef9764a02 100644 --- a/kernel/drivers/media/dvb-frontends/zl10353.c +++ b/kernel/drivers/media/dvb-frontends/zl10353.c @@ -462,7 +462,7 @@ static int zl10353_get_parameters(struct dvb_frontend *fe) return 0; } -static int zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int zl10353_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct zl10353_state *state = fe->demodulator_priv; int s6, s7, s8; @@ -533,13 +533,13 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct zl10353_state *state = fe->demodulator_priv; - u32 ubl = 0; + u32 ubl = 0; - ubl = zl10353_read_register(state, RS_UBC_1) << 8 | - zl10353_read_register(state, RS_UBC_0); + ubl = zl10353_read_register(state, RS_UBC_1) << 8 | + zl10353_read_register(state, RS_UBC_0); - state->ucblocks += ubl; - *ucblocks = state->ucblocks; + state->ucblocks += ubl; + *ucblocks = state->ucblocks; return 0; } diff --git a/kernel/drivers/media/firewire/firedtv-fe.c b/kernel/drivers/media/firewire/firedtv-fe.c index 6fe9793b9..17acda6bc 100644 --- a/kernel/drivers/media/firewire/firedtv-fe.c +++ b/kernel/drivers/media/firewire/firedtv-fe.c @@ -61,12 +61,12 @@ static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, } static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, - fe_sec_mini_cmd_t minicmd) + enum fe_sec_mini_cmd minicmd) { return 0; } -static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int fdtv_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct firedtv *fdtv = fe->sec_priv; @@ -75,7 +75,7 @@ static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) } static int fdtv_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct firedtv *fdtv = fe->sec_priv; @@ -83,7 +83,7 @@ static int fdtv_set_voltage(struct dvb_frontend *fe, return 0; } -static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int fdtv_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct firedtv *fdtv = fe->sec_priv; struct firedtv_tuner_status stat; diff --git a/kernel/drivers/media/firewire/firedtv.h b/kernel/drivers/media/firewire/firedtv.h index 346a85be6..345d1eda8 100644 --- a/kernel/drivers/media/firewire/firedtv.h +++ b/kernel/drivers/media/firewire/firedtv.h @@ -99,8 +99,8 @@ struct firedtv { s8 isochannel; struct fdtv_ir_context *ir_context; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone; + enum fe_sec_voltage voltage; + enum fe_sec_tone_mode tone; struct mutex demux_mutex; unsigned long channel_active; diff --git a/kernel/drivers/media/i2c/Kconfig b/kernel/drivers/media/i2c/Kconfig index 6f30ea761..521bbf1b2 100644 --- a/kernel/drivers/media/i2c/Kconfig +++ b/kernel/drivers/media/i2c/Kconfig @@ -22,7 +22,7 @@ config VIDEO_IR_I2C # menu "Encoders, decoders, sensors and other helper chips" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST comment "Audio decoders, processors and mixers" @@ -197,6 +197,8 @@ config VIDEO_ADV7183 config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB || COMPILE_TEST + select HDMI ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -285,6 +287,16 @@ config VIDEO_SAA711X To compile this driver as a module, choose M here: the module will be called saa7115. +config VIDEO_TC358743 + tristate "Toshiba TC358743 decoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + select HDMI + ---help--- + Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. + + To compile this driver as a module, choose M here: the + module will be called tc358743. + config VIDEO_TVP514X tristate "Texas Instruments TVP514x video decoder" depends on VIDEO_V4L2 && I2C @@ -424,6 +436,7 @@ config VIDEO_ADV7393 config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + select HDMI ---help--- Support for the Analog Devices ADV7511 video encoder. diff --git a/kernel/drivers/media/i2c/Makefile b/kernel/drivers/media/i2c/Makefile index f165faea5..07db257ab 100644 --- a/kernel/drivers/media/i2c/Makefile +++ b/kernel/drivers/media/i2c/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_VIDEO_AK881X) += ak881x.o 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 diff --git a/kernel/drivers/media/i2c/adp1653.c b/kernel/drivers/media/i2c/adp1653.c index 873fe1949..5dd39775d 100644 --- a/kernel/drivers/media/i2c/adp1653.c +++ b/kernel/drivers/media/i2c/adp1653.c @@ -8,6 +8,7 @@ * Contributors: * Sakari Ailus * Tuukka Toivonen + * Pavel Machek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -308,16 +311,28 @@ __adp1653_set_power(struct adp1653_flash *flash, int on) { int ret; - ret = flash->platform_data->power(&flash->subdev, on); - if (ret < 0) - return ret; + if (flash->platform_data->power) { + ret = flash->platform_data->power(&flash->subdev, on); + if (ret < 0) + return ret; + } else { + gpiod_set_value(flash->platform_data->enable_gpio, on); + if (on) + /* Some delay is apparently required. */ + udelay(20); + } if (!on) return 0; ret = adp1653_init_device(flash); - if (ret < 0) + if (ret >= 0) + return ret; + + if (flash->platform_data->power) flash->platform_data->power(&flash->subdev, 0); + else + gpiod_set_value(flash->platform_data->enable_gpio, 0); return ret; } @@ -407,21 +422,85 @@ static int adp1653_resume(struct device *dev) #endif /* CONFIG_PM */ +static int adp1653_of_init(struct i2c_client *client, + struct adp1653_flash *flash, + struct device_node *node) +{ + struct adp1653_platform_data *pd; + struct device_node *child; + + pd = devm_kzalloc(&client->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + flash->platform_data = pd; + + child = of_get_child_by_name(node, "flash"); + if (!child) + return -EINVAL; + + if (of_property_read_u32(child, "flash-timeout-us", + &pd->max_flash_timeout)) + goto err; + + if (of_property_read_u32(child, "flash-max-microamp", + &pd->max_flash_intensity)) + goto err; + + pd->max_flash_intensity /= 1000; + + if (of_property_read_u32(child, "led-max-microamp", + &pd->max_torch_intensity)) + goto err; + + pd->max_torch_intensity /= 1000; + of_node_put(child); + + child = of_get_child_by_name(node, "indicator"); + if (!child) + return -EINVAL; + + if (of_property_read_u32(child, "led-max-microamp", + &pd->max_indicator_intensity)) + goto err; + + of_node_put(child); + + pd->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW); + if (!pd->enable_gpio) { + dev_err(&client->dev, "Error getting GPIO\n"); + return -EINVAL; + } + + return 0; +err: + dev_err(&client->dev, "Required property not found\n"); + of_node_put(child); + return -EINVAL; +} + + static int adp1653_probe(struct i2c_client *client, const struct i2c_device_id *devid) { struct adp1653_flash *flash; int ret; - /* we couldn't work without platform data */ - if (client->dev.platform_data == NULL) - return -ENODEV; - flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); if (flash == NULL) return -ENOMEM; - flash->platform_data = client->dev.platform_data; + if (client->dev.of_node) { + ret = adp1653_of_init(client, flash, client->dev.of_node); + if (ret) + return ret; + } else { + if (!client->dev.platform_data) { + dev_err(&client->dev, + "Neither DT not platform data provided\n"); + return EINVAL; + } + flash->platform_data = client->dev.platform_data; + } mutex_init(&flash->power_lock); @@ -442,6 +521,7 @@ static int adp1653_probe(struct i2c_client *client, return 0; free_and_quit: + dev_err(&client->dev, "adp1653: failed to register device\n"); v4l2_ctrl_handler_free(&flash->ctrls); return ret; } @@ -464,7 +544,7 @@ static const struct i2c_device_id adp1653_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, adp1653_id_table); -static struct dev_pm_ops adp1653_pm_ops = { +static const struct dev_pm_ops adp1653_pm_ops = { .suspend = adp1653_suspend, .resume = adp1653_resume, }; diff --git a/kernel/drivers/media/i2c/adv7170.c b/kernel/drivers/media/i2c/adv7170.c index 40a1a95c7..05f1dc6c7 100644 --- a/kernel/drivers/media/i2c/adv7170.c +++ b/kernel/drivers/media/i2c/adv7170.c @@ -262,21 +262,27 @@ static int adv7170_s_routing(struct v4l2_subdev *sd, return 0; } -static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int adv7170_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(adv7170_codes)) + if (code->pad || code->index >= ARRAY_SIZE(adv7170_codes)) return -EINVAL; - *code = adv7170_codes[index]; + code->code = adv7170_codes[code->index]; return 0; } -static int adv7170_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int adv7170_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; u8 val = adv7170_read(sd, 0x7); + if (format->pad) + return -EINVAL; + if ((val & 0x40) == (1 << 6)) mf->code = MEDIA_BUS_FMT_UYVY8_1X16; else @@ -290,11 +296,16 @@ static int adv7170_g_fmt(struct v4l2_subdev *sd, return 0; } -static int adv7170_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int adv7170_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; u8 val = adv7170_read(sd, 0x7); - int ret; + int ret = 0; + + if (format->pad) + return -EINVAL; switch (mf->code) { case MEDIA_BUS_FMT_UYVY8_2X8: @@ -311,7 +322,8 @@ static int adv7170_s_fmt(struct v4l2_subdev *sd, return -EINVAL; } - ret = adv7170_write(sd, 0x7, val); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = adv7170_write(sd, 0x7, val); return ret; } @@ -321,13 +333,17 @@ static int adv7170_s_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops adv7170_video_ops = { .s_std_output = adv7170_s_std_output, .s_routing = adv7170_s_routing, - .s_mbus_fmt = adv7170_s_fmt, - .g_mbus_fmt = adv7170_g_fmt, - .enum_mbus_fmt = adv7170_enum_fmt, +}; + +static const struct v4l2_subdev_pad_ops adv7170_pad_ops = { + .enum_mbus_code = adv7170_enum_mbus_code, + .get_fmt = adv7170_get_fmt, + .set_fmt = adv7170_set_fmt, }; static const struct v4l2_subdev_ops adv7170_ops = { .video = &adv7170_video_ops, + .pad = &adv7170_pad_ops, }; /* ----------------------------------------------------------------------- */ @@ -385,7 +401,6 @@ MODULE_DEVICE_TABLE(i2c, adv7170_id); static struct i2c_driver adv7170_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7170", }, .probe = adv7170_probe, diff --git a/kernel/drivers/media/i2c/adv7175.c b/kernel/drivers/media/i2c/adv7175.c index d220af579..f554809a5 100644 --- a/kernel/drivers/media/i2c/adv7175.c +++ b/kernel/drivers/media/i2c/adv7175.c @@ -300,21 +300,27 @@ static int adv7175_s_routing(struct v4l2_subdev *sd, return 0; } -static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int adv7175_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(adv7175_codes)) + if (code->pad || code->index >= ARRAY_SIZE(adv7175_codes)) return -EINVAL; - *code = adv7175_codes[index]; + code->code = adv7175_codes[code->index]; return 0; } -static int adv7175_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int adv7175_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; u8 val = adv7175_read(sd, 0x7); + if (format->pad) + return -EINVAL; + if ((val & 0x40) == (1 << 6)) mf->code = MEDIA_BUS_FMT_UYVY8_1X16; else @@ -328,11 +334,16 @@ static int adv7175_g_fmt(struct v4l2_subdev *sd, return 0; } -static int adv7175_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int adv7175_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; u8 val = adv7175_read(sd, 0x7); - int ret; + int ret = 0; + + if (format->pad) + return -EINVAL; switch (mf->code) { case MEDIA_BUS_FMT_UYVY8_2X8: @@ -349,7 +360,8 @@ static int adv7175_s_fmt(struct v4l2_subdev *sd, return -EINVAL; } - ret = adv7175_write(sd, 0x7, val); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ret = adv7175_write(sd, 0x7, val); return ret; } @@ -374,14 +386,18 @@ static const struct v4l2_subdev_core_ops adv7175_core_ops = { static const struct v4l2_subdev_video_ops adv7175_video_ops = { .s_std_output = adv7175_s_std_output, .s_routing = adv7175_s_routing, - .s_mbus_fmt = adv7175_s_fmt, - .g_mbus_fmt = adv7175_g_fmt, - .enum_mbus_fmt = adv7175_enum_fmt, +}; + +static const struct v4l2_subdev_pad_ops adv7175_pad_ops = { + .enum_mbus_code = adv7175_enum_mbus_code, + .get_fmt = adv7175_get_fmt, + .set_fmt = adv7175_set_fmt, }; static const struct v4l2_subdev_ops adv7175_ops = { .core = &adv7175_core_ops, .video = &adv7175_video_ops, + .pad = &adv7175_pad_ops, }; /* ----------------------------------------------------------------------- */ @@ -439,7 +455,6 @@ MODULE_DEVICE_TABLE(i2c, adv7175_id); static struct i2c_driver adv7175_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7175", }, .probe = adv7175_probe, diff --git a/kernel/drivers/media/i2c/adv7180.c b/kernel/drivers/media/i2c/adv7180.c index a493c0b0b..f82c8aa16 100644 --- a/kernel/drivers/media/i2c/adv7180.c +++ b/kernel/drivers/media/i2c/adv7180.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1324,11 +1325,20 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #define ADV7180_PM_OPS NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id adv7180_of_id[] = { + { .compatible = "adi,adv7180", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, adv7180_of_id); +#endif + static struct i2c_driver adv7180_driver = { .driver = { - .owner = THIS_MODULE, .name = KBUILD_MODNAME, .pm = ADV7180_PM_OPS, + .of_match_table = of_match_ptr(adv7180_of_id), }, .probe = adv7180_probe, .remove = adv7180_remove, diff --git a/kernel/drivers/media/i2c/adv7183.c b/kernel/drivers/media/i2c/adv7183.c index 28940cc3a..e2dd16176 100644 --- a/kernel/drivers/media/i2c/adv7183.c +++ b/kernel/drivers/media/i2c/adv7183.c @@ -420,20 +420,26 @@ static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) return 0; } -static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) +static int adv7183_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index > 0) + if (code->pad || code->index > 0) return -EINVAL; - *code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_2X8; return 0; } -static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int adv7183_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct adv7183 *decoder = to_adv7183(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + if (format->pad) + return -EINVAL; fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -446,25 +452,23 @@ static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, fmt->width = 720; fmt->height = 576; } + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + decoder->fmt = *fmt; + else + cfg->try_fmt = *fmt; return 0; } -static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int adv7183_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct adv7183 *decoder = to_adv7183(sd); - adv7183_try_mbus_fmt(sd, fmt); - decoder->fmt = *fmt; - return 0; -} - -static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct adv7183 *decoder = to_adv7183(sd); + if (format->pad) + return -EINVAL; - *fmt = decoder->fmt; + format->format = decoder->fmt; return 0; } @@ -514,16 +518,19 @@ static const struct v4l2_subdev_video_ops adv7183_video_ops = { .s_routing = adv7183_s_routing, .querystd = adv7183_querystd, .g_input_status = adv7183_g_input_status, - .enum_mbus_fmt = adv7183_enum_mbus_fmt, - .try_mbus_fmt = adv7183_try_mbus_fmt, - .s_mbus_fmt = adv7183_s_mbus_fmt, - .g_mbus_fmt = adv7183_g_mbus_fmt, .s_stream = adv7183_s_stream, }; +static const struct v4l2_subdev_pad_ops adv7183_pad_ops = { + .enum_mbus_code = adv7183_enum_mbus_code, + .get_fmt = adv7183_get_fmt, + .set_fmt = adv7183_set_fmt, +}; + static const struct v4l2_subdev_ops adv7183_ops = { .core = &adv7183_core_ops, .video = &adv7183_video_ops, + .pad = &adv7183_pad_ops, }; static int adv7183_probe(struct i2c_client *client, @@ -533,7 +540,9 @@ static int adv7183_probe(struct i2c_client *client, struct v4l2_subdev *sd; struct v4l2_ctrl_handler *hdl; int ret; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; const unsigned *pin_array; /* Check if the adapter supports the needed features */ @@ -603,9 +612,9 @@ static int adv7183_probe(struct i2c_client *client, adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); adv7183_s_std(sd, decoder->std); - fmt.width = 720; - fmt.height = 576; - adv7183_s_mbus_fmt(sd, &fmt); + fmt.format.width = 720; + fmt.format.height = 576; + adv7183_set_fmt(sd, NULL, &fmt); /* initialize the hardware to the default control values */ ret = v4l2_ctrl_handler_setup(hdl); diff --git a/kernel/drivers/media/i2c/adv7343.c b/kernel/drivers/media/i2c/adv7343.c index 7c50833e7..f89d0afcd 100644 --- a/kernel/drivers/media/i2c/adv7343.c +++ b/kernel/drivers/media/i2c/adv7343.c @@ -319,13 +319,6 @@ static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7343_core_ops = { .log_status = adv7343_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) @@ -529,7 +522,6 @@ MODULE_DEVICE_TABLE(of, adv7343_of_match); static struct i2c_driver adv7343_driver = { .driver = { .of_match_table = of_match_ptr(adv7343_of_match), - .owner = THIS_MODULE, .name = "adv7343", }, .probe = adv7343_probe, diff --git a/kernel/drivers/media/i2c/adv7393.c b/kernel/drivers/media/i2c/adv7393.c index 558f19154..0215f95c2 100644 --- a/kernel/drivers/media/i2c/adv7393.c +++ b/kernel/drivers/media/i2c/adv7393.c @@ -306,13 +306,6 @@ static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7393_core_ops = { .log_status = adv7393_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) diff --git a/kernel/drivers/media/i2c/adv7511.c b/kernel/drivers/media/i2c/adv7511.c index 12d93203d..e4900df11 100644 --- a/kernel/drivers/media/i2c/adv7511.c +++ b/kernel/drivers/media/i2c/adv7511.c @@ -40,7 +40,7 @@ MODULE_PARM_DESC(debug, "debug level (0-2)"); MODULE_DESCRIPTION("Analog Devices ADV7511 HDMI Transmitter Device Driver"); MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); #define MASK_ADV7511_EDID_RDY_INT 0x04 #define MASK_ADV7511_MSEN_INT 0x40 @@ -77,7 +77,7 @@ struct adv7511_state_edid { u32 blocks; /* Number of segments read */ u32 segments; - uint8_t data[EDID_MAX_SEGM * 256]; + u8 data[EDID_MAX_SEGM * 256]; /* Number of EDID read retries left */ unsigned read_retries; bool complete; @@ -89,8 +89,9 @@ struct adv7511_state { struct media_pad pad; struct v4l2_ctrl_handler hdl; int chip_revision; - uint8_t i2c_edid_addr; - uint8_t i2c_cec_addr; + u8 i2c_edid_addr; + u8 i2c_cec_addr; + u8 i2c_pktmem_addr; /* Is the adv7511 powered on? */ bool power_on; /* Did we receive hotplug and rx-sense signals? */ @@ -101,6 +102,7 @@ struct adv7511_state { u32 colorspace; u32 ycbcr_enc; u32 quantization; + u32 xfer_func; /* controls */ struct v4l2_ctrl *hdmi_mode_ctrl; struct v4l2_ctrl *hotplug_ctrl; @@ -108,6 +110,7 @@ struct adv7511_state { struct v4l2_ctrl *have_edid0_ctrl; struct v4l2_ctrl *rgb_quantization_range_ctrl; struct i2c_client *i2c_edid; + struct i2c_client *i2c_pktmem; struct adv7511_state_edid edid; /* Running counter of the number of detected EDIDs (for debugging) */ unsigned edid_detect_counter; @@ -200,7 +203,7 @@ static int adv7511_wr(struct v4l2_subdev *sd, u8 reg, u8 val) /* To set specific bits in the register, a clear-mask is given (to be AND-ed), and then the value-mask (to be OR-ed). */ -static inline void adv7511_wr_and_or(struct v4l2_subdev *sd, u8 reg, uint8_t clr_mask, uint8_t val_mask) +static inline void adv7511_wr_and_or(struct v4l2_subdev *sd, u8 reg, u8 clr_mask, u8 val_mask) { adv7511_wr(sd, reg, (adv7511_rd(sd, reg) & clr_mask) | val_mask); } @@ -222,7 +225,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client, return ret; } -static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) +static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) { struct adv7511_state *state = get_adv7511_state(sd); int i; @@ -237,6 +240,35 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t v4l2_err(sd, "%s: i2c read error\n", __func__); } +static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + return adv_smbus_read_byte_data(state->i2c_pktmem, reg); +} + +static int adv7511_pktmem_wr(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(state->i2c_pktmem, reg, val); + if (ret == 0) + return 0; + } + v4l2_err(sd, "%s: i2c write error\n", __func__); + return ret; +} + +/* To set specific bits in the register, a clear-mask is given (to be AND-ed), + and then the value-mask (to be OR-ed). */ +static inline void adv7511_pktmem_wr_and_or(struct v4l2_subdev *sd, u8 reg, u8 clr_mask, u8 val_mask) +{ + adv7511_pktmem_wr(sd, reg, (adv7511_pktmem_rd(sd, reg) & clr_mask) | val_mask); +} + static inline bool adv7511_have_hotplug(struct v4l2_subdev *sd) { return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT; @@ -247,7 +279,7 @@ static inline bool adv7511_have_rx_sense(struct v4l2_subdev *sd) return adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT; } -static void adv7511_csc_conversion_mode(struct v4l2_subdev *sd, uint8_t mode) +static void adv7511_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode) { adv7511_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); } @@ -291,7 +323,7 @@ static void adv7511_csc_coeff(struct v4l2_subdev *sd, static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) { if (enable) { - uint8_t csc_mode = 0; + u8 csc_mode = 0; adv7511_csc_conversion_mode(sd, csc_mode); adv7511_csc_coeff(sd, 4096-564, 0, 0, 256, @@ -414,6 +446,80 @@ static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regi } #endif +struct adv7511_cfg_read_infoframe { + const char *desc; + u8 present_reg; + u8 present_mask; + u8 header[3]; + u16 payload_addr; +}; + +static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) +{ + u8 csum = 0; + size_t i; + + /* compute checksum */ + for (i = 0; i < size; i++) + csum += ptr[i]; + + return 256 - csum; +} + +static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_infoframe *cri) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + union hdmi_infoframe frame; + u8 buffer[32]; + u8 len; + int i; + + if (!(adv7511_rd(sd, cri->present_reg) & cri->present_mask)) { + v4l2_info(sd, "%s infoframe not transmitted\n", cri->desc); + return; + } + + memcpy(buffer, cri->header, sizeof(cri->header)); + + len = buffer[2]; + + if (len + 4 > sizeof(buffer)) { + v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len); + return; + } + + if (cri->payload_addr >= 0x100) { + for (i = 0; i < len; i++) + buffer[i + 4] = adv7511_pktmem_rd(sd, cri->payload_addr + i - 0x100); + } else { + for (i = 0; i < len; i++) + buffer[i + 4] = adv7511_rd(sd, cri->payload_addr + i); + } + buffer[3] = 0; + buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); + + if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); + return; + } + + hdmi_infoframe_log(KERN_INFO, dev, &frame); +} + +static void adv7511_log_infoframes(struct v4l2_subdev *sd) +{ + static const struct adv7511_cfg_read_infoframe cri[] = { + { "AVI", 0x44, 0x10, { 0x82, 2, 13 }, 0x55 }, + { "Audio", 0x44, 0x08, { 0x84, 1, 10 }, 0x73 }, + { "SDP", 0x40, 0x40, { 0x83, 1, 25 }, 0x103 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(cri); i++) + log_infoframe(sd, &cri[i]); +} + static int adv7511_log_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); @@ -479,6 +585,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) manual_cts ? "manual" : "automatic", N, CTS); v4l2_info(sd, "VIC: detected %d, sent %d\n", vic_detect, vic_sent); + adv7511_log_infoframes(sd); } if (state->dv_timings.type == V4L2_DV_BT_656_1120) v4l2_print_dv_timings(sd->name, "timings: ", @@ -487,6 +594,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "no timings set\n"); v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); + v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr); return 0; } @@ -536,6 +644,7 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) adv7511_wr(sd, 0xf9, 0x00); adv7511_wr(sd, 0x43, state->i2c_edid_addr); + adv7511_wr(sd, 0x45, state->i2c_pktmem_addr); /* Set number of attempts to read the EDID */ adv7511_wr(sd, 0xc9, 0xf); @@ -545,8 +654,8 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) /* Enable interrupts */ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) { - uint8_t irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; - uint8_t irqs_rd; + u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; + u8 irqs_rd; int retries = 100; v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); @@ -579,7 +688,7 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) /* Interrupt handler */ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { - uint8_t irq_status; + u8 irq_status; /* disable interrupts to prevent a race condition */ adv7511_set_isr(sd, false); @@ -861,11 +970,13 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd, format->format.colorspace = fmt->colorspace; format->format.ycbcr_enc = fmt->ycbcr_enc; format->format.quantization = fmt->quantization; + format->format.xfer_func = fmt->xfer_func; } else { format->format.code = state->fmt_code; format->format.colorspace = state->colorspace; format->format.ycbcr_enc = state->ycbcr_enc; format->format.quantization = state->quantization; + format->format.xfer_func = state->xfer_func; } return 0; @@ -912,6 +1023,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, fmt->colorspace = format->format.colorspace; fmt->ycbcr_enc = format->format.ycbcr_enc; fmt->quantization = format->format.quantization; + fmt->xfer_func = format->format.xfer_func; return 0; } @@ -936,6 +1048,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, state->colorspace = format->format.colorspace; state->ycbcr_enc = format->format.ycbcr_enc; state->quantization = format->format.quantization; + state->xfer_func = format->format.xfer_func; switch (format->format.colorspace) { case V4L2_COLORSPACE_ADOBERGB: @@ -1028,7 +1141,7 @@ static const struct v4l2_subdev_ops adv7511_ops = { }; /* ----------------------------------------------------------------------- */ -static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, int segment, uint8_t *buf) +static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, int segment, u8 *buf) { if (debug >= lvl) { int i, j; @@ -1140,7 +1253,7 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); /* read hotplug and rx-sense state */ - uint8_t status = adv7511_rd(sd, 0x42); + u8 status = adv7511_rd(sd, 0x42); v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", __func__, @@ -1184,9 +1297,9 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) } } -static bool edid_block_verify_crc(uint8_t *edid_block) +static bool edid_block_verify_crc(u8 *edid_block) { - uint8_t sum = 0; + u8 sum = 0; int i; for (i = 0; i < 128; i++) @@ -1198,7 +1311,7 @@ static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) { struct adv7511_state *state = get_adv7511_state(sd); u32 blocks = state->edid.blocks; - uint8_t *data = state->edid.data; + u8 *data = state->edid.data; if (!edid_block_verify_crc(&data[segment * 256])) return false; @@ -1223,7 +1336,7 @@ static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) static bool adv7511_check_edid_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); - uint8_t edidRdy = adv7511_rd(sd, 0xc5); + u8 edidRdy = adv7511_rd(sd, 0xc5); v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", __func__, EDID_MAX_RETRIES - state->edid.read_retries); @@ -1376,6 +1489,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * /* EDID and CEC i2c addr */ state->i2c_edid_addr = state->pdata.i2c_edid << 1; state->i2c_cec_addr = state->pdata.i2c_cec << 1; + state->i2c_pktmem_addr = state->pdata.i2c_pktmem << 1; state->chip_revision = adv7511_rd(sd, 0x0); chip_id[0] = adv7511_rd(sd, 0xf5); @@ -1393,12 +1507,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * goto err_entity; } + state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1); + if (state->i2c_pktmem == NULL) { + v4l2_err(sd, "failed to register pktmem i2c client\n"); + err = -ENOMEM; + goto err_unreg_edid; + } + adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ state->work_queue = create_singlethread_workqueue(sd->name); if (state->work_queue == NULL) { v4l2_err(sd, "could not create workqueue\n"); err = -ENOMEM; - goto err_unreg_cec; + goto err_unreg_pktmem; } INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); @@ -1411,7 +1532,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * client->addr << 1, client->adapter->name); return 0; -err_unreg_cec: +err_unreg_pktmem: + i2c_unregister_device(state->i2c_pktmem); +err_unreg_edid: i2c_unregister_device(state->i2c_edid); err_entity: media_entity_cleanup(&sd->entity); @@ -1435,6 +1558,7 @@ static int adv7511_remove(struct i2c_client *client) adv7511_init_setup(sd); cancel_delayed_work(&state->edid_handler); i2c_unregister_device(state->i2c_edid); + i2c_unregister_device(state->i2c_pktmem); destroy_workqueue(state->work_queue); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); @@ -1452,7 +1576,6 @@ MODULE_DEVICE_TABLE(i2c, adv7511_id); static struct i2c_driver adv7511_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7511", }, .probe = adv7511_probe, diff --git a/kernel/drivers/media/i2c/adv7604.c b/kernel/drivers/media/i2c/adv7604.c index 60ffcf098..01adcdc52 100644 --- a/kernel/drivers/media/i2c/adv7604.c +++ b/kernel/drivers/media/i2c/adv7604.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -36,10 +37,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -80,6 +83,7 @@ MODULE_LICENSE("GPL"); enum adv76xx_type { ADV7604, ADV7611, + ADV7612, }; struct adv76xx_reg_seq { @@ -95,6 +99,13 @@ struct adv76xx_format_info { u8 op_format_sel; }; +struct adv76xx_cfg_read_infoframe { + const char *desc; + u8 present_mask; + u8 head_addr; + u8 payload_addr; +}; + struct adv76xx_chip_info { enum adv76xx_type type; @@ -124,6 +135,20 @@ struct adv76xx_chip_info { unsigned int num_recommended_settings[2]; unsigned long page_mask; + + /* Masks for timings */ + unsigned int linewidth_mask; + unsigned int field0_height_mask; + unsigned int field1_height_mask; + unsigned int hfrontporch_mask; + unsigned int hsync_mask; + unsigned int hbackporch_mask; + unsigned int field0_vfrontporch_mask; + unsigned int field1_vfrontporch_mask; + unsigned int field0_vsync_mask; + unsigned int field1_vsync_mask; + unsigned int field0_vbackporch_mask; + unsigned int field1_vbackporch_mask; }; /* @@ -166,6 +191,9 @@ struct adv76xx_state { /* i2c clients */ struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX]; + /* Regmaps */ + struct regmap *regmap[ADV76XX_PAGE_MAX]; + /* controls */ struct v4l2_ctrl *detect_tx_5v_ctrl; struct v4l2_ctrl *analog_sampling_phase_ctrl; @@ -327,6 +355,11 @@ static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr[] = { { }, }; +static const struct v4l2_event adv76xx_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + /* ----------------------------------------------------------------------- */ static inline struct adv76xx_state *to_state(struct v4l2_subdev *sd) @@ -346,66 +379,39 @@ static inline unsigned vtotal(const struct v4l2_bt_timings *t) /* ----------------------------------------------------------------------- */ -static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, - u8 command, bool check) -{ - union i2c_smbus_data data; - - if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, - I2C_SMBUS_READ, command, - I2C_SMBUS_BYTE_DATA, &data)) - return data.byte; - if (check) - v4l_err(client, "error reading %02x, %02x\n", - client->addr, command); - return -EIO; -} - -static s32 adv_smbus_read_byte_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command) -{ - return adv_smbus_read_byte_data_check(state->i2c_clients[page], - command, true); -} - -static s32 adv_smbus_write_byte_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command, - u8 value) +static int adv76xx_read_check(struct adv76xx_state *state, + int client_page, u8 reg) { - struct i2c_client *client = state->i2c_clients[page]; - union i2c_smbus_data data; + struct i2c_client *client = state->i2c_clients[client_page]; int err; - int i; + unsigned int val; - data.byte = value; - for (i = 0; i < 3; i++) { - err = i2c_smbus_xfer(client->adapter, client->addr, - client->flags, - I2C_SMBUS_WRITE, command, - I2C_SMBUS_BYTE_DATA, &data); - if (!err) - break; + err = regmap_read(state->regmap[client_page], reg, &val); + + if (err) { + v4l_err(client, "error reading %02x, %02x\n", + client->addr, reg); + return err; } - if (err < 0) - v4l_err(client, "error writing %02x, %02x, %02x\n", - client->addr, command, value); - return err; + return val; } -static s32 adv_smbus_write_i2c_block_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command, - unsigned length, const u8 *values) +/* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX + * size to one or more registers. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +static int adv76xx_write_block(struct adv76xx_state *state, int client_page, + unsigned int init_reg, const void *val, + size_t val_len) { - struct i2c_client *client = state->i2c_clients[page]; - union i2c_smbus_data data; + struct regmap *regmap = state->regmap[client_page]; + + if (val_len > I2C_SMBUS_BLOCK_MAX) + val_len = I2C_SMBUS_BLOCK_MAX; - if (length > I2C_SMBUS_BLOCK_MAX) - length = I2C_SMBUS_BLOCK_MAX; - data.block[0] = length; - memcpy(data.block + 1, values, length); - return i2c_smbus_xfer(client->adapter, client->addr, client->flags, - I2C_SMBUS_WRITE, command, - I2C_SMBUS_I2C_BLOCK_DATA, &data); + return regmap_raw_write(regmap, init_reg, val, val_len); } /* ----------------------------------------------------------------------- */ @@ -414,14 +420,14 @@ static inline int io_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_IO, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_IO, reg); } static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); } static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -433,71 +439,70 @@ static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg); + return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg); } static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val); + return regmap_write(state->regmap[ADV7604_PAGE_AVLINK], reg, val); } static inline int cec_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CEC, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_CEC, reg); } static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); } static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg); } static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_INFOFRAME, - reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val); } static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_AFE, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_AFE, reg); } static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_AFE, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_AFE], reg, val); } static inline int rep_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_REP, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_REP, reg); } static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_REP, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_REP], reg, val); } static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -509,28 +514,37 @@ static inline int edid_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_EDID, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg); } static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_EDID, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_EDID], reg, val); } static inline int edid_write_block(struct v4l2_subdev *sd, - unsigned len, const u8 *val) + unsigned int total_len, const u8 *val) { struct adv76xx_state *state = to_state(sd); int err = 0; - int i; + int i = 0; + int len = 0; + + v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", + __func__, total_len); + + while (!err && i < total_len) { + len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ? + I2C_SMBUS_BLOCK_MAX : + (total_len - i); - v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); + err = adv76xx_write_block(state, ADV76XX_PAGE_EDID, + i, val + i, len); + i += len; + } - for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) - err = adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_EDID, - i, I2C_SMBUS_BLOCK_MAX, val + i); return err; } @@ -560,7 +574,7 @@ static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_HDMI, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_HDMI, reg); } static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -572,7 +586,7 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_HDMI, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_HDMI], reg, val); } static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -584,14 +598,14 @@ static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_TEST, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_TEST], reg, val); } static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CP, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_CP, reg); } static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -603,7 +617,7 @@ static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CP, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_CP], reg, val); } static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -615,14 +629,14 @@ static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg); + return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg); } static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); + return regmap_write(state->regmap[ADV7604_PAGE_VDP], reg, val); } #define ADV76XX_REG(page, offset) (((page) << 8) | (offset)) @@ -633,13 +647,16 @@ static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg) { struct adv76xx_state *state = to_state(sd); unsigned int page = reg >> 8; + unsigned int val; + int err; if (!(BIT(page) & state->info->page_mask)) return -EINVAL; reg &= 0xff; + err = regmap_read(state->regmap[page], reg, &val); - return adv_smbus_read_byte_data(state, page, reg); + return err ? err : val; } #endif @@ -653,7 +670,7 @@ static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) reg &= 0xff; - return adv_smbus_write_byte_data(state, page, reg, val); + return regmap_write(state->regmap[page], reg, val); } static void adv76xx_write_reg_seq(struct v4l2_subdev *sd, @@ -739,6 +756,23 @@ static const struct adv76xx_format_info adv7611_formats[] = { ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, }; +static const struct adv76xx_format_info adv7612_formats[] = { + { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, + ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, +}; + static const struct adv76xx_format_info * adv76xx_format_info(struct adv76xx_state *state, u32 code) { @@ -843,6 +877,16 @@ static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) return value & 1; } +static unsigned int adv7612_read_cable_det(struct v4l2_subdev *sd) +{ + /* Reads CABLE_DET_A_RAW. For input B support, need to + * account for bit 7 [MSB] of 0x6a (ie. CABLE_DET_B_RAW) + */ + u8 value = io_read(sd, 0x6f); + + return value & 1; +} + static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -949,8 +993,8 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_IO, - 0x16, 2, pll)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_IO], + 0x16, pll, 2)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); /* active video - horizontal timing */ @@ -1001,8 +1045,8 @@ static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off offset_buf[3] = offset_c & 0x0ff; /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, - 0x77, 4, offset_buf)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], + 0x77, offset_buf, 4)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); } @@ -1031,8 +1075,8 @@ static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, gain_buf[3] = ((gain_c & 0x0ff)); /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, - 0x73, 4, gain_buf)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], + 0x73, gain_buf, 4)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); } @@ -1301,15 +1345,15 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, } } - if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - timings)) + false, timings)) return 0; if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - state->aspect_ratio, timings)) + false, state->aspect_ratio, timings)) return 0; v4l2_dbg(2, debug, sd, @@ -1504,23 +1548,28 @@ static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, if (is_digital_input(sd)) { timings->type = V4L2_DV_BT_656_1120; - /* FIXME: All masks are incorrect for ADV7611 */ - bt->width = hdmi_read16(sd, 0x07, 0xfff); - bt->height = hdmi_read16(sd, 0x09, 0xfff); + bt->width = hdmi_read16(sd, 0x07, info->linewidth_mask); + bt->height = hdmi_read16(sd, 0x09, info->field0_height_mask); bt->pixelclock = info->read_hdmi_pixelclock(sd); - bt->hfrontporch = hdmi_read16(sd, 0x20, 0x3ff); - bt->hsync = hdmi_read16(sd, 0x22, 0x3ff); - bt->hbackporch = hdmi_read16(sd, 0x24, 0x3ff); - bt->vfrontporch = hdmi_read16(sd, 0x2a, 0x1fff) / 2; - bt->vsync = hdmi_read16(sd, 0x2e, 0x1fff) / 2; - bt->vbackporch = hdmi_read16(sd, 0x32, 0x1fff) / 2; + bt->hfrontporch = hdmi_read16(sd, 0x20, info->hfrontporch_mask); + bt->hsync = hdmi_read16(sd, 0x22, info->hsync_mask); + bt->hbackporch = hdmi_read16(sd, 0x24, info->hbackporch_mask); + bt->vfrontporch = hdmi_read16(sd, 0x2a, + info->field0_vfrontporch_mask) / 2; + bt->vsync = hdmi_read16(sd, 0x2e, info->field0_vsync_mask) / 2; + bt->vbackporch = hdmi_read16(sd, 0x32, + info->field0_vbackporch_mask) / 2; bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); if (bt->interlaced == V4L2_DV_INTERLACED) { - bt->height += hdmi_read16(sd, 0x0b, 0xfff); - bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2; - bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2; - bt->il_vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2; + bt->height += hdmi_read16(sd, 0x0b, + info->field1_height_mask); + bt->il_vfrontporch = hdmi_read16(sd, 0x2c, + info->field1_vfrontporch_mask) / 2; + bt->il_vsync = hdmi_read16(sd, 0x30, + info->field1_vsync_mask) / 2; + bt->il_vbackporch = hdmi_read16(sd, 0x34, + info->field1_vbackporch_mask) / 2; } adv76xx_fill_optional_dv_timings_fields(sd, timings); } else { @@ -1725,11 +1774,11 @@ static int adv76xx_s_routing(struct v4l2_subdev *sd, state->selected_input = input; disable_input(sd); - select_input(sd); - enable_input(sd); + v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); + return 0; } @@ -1896,7 +1945,7 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", __func__, fmt_change, fmt_change_digital); - v4l2_subdev_notify(sd, ADV76XX_FMT_CHANGE, NULL); + v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); if (handled) *handled = true; @@ -1911,10 +1960,9 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } /* tx 5v detect */ - tx_5v = io_read(sd, 0x70) & info->cable_det_mask; + tx_5v = irq_reg_0x70 & info->cable_det_mask; if (tx_5v) { v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); - io_write(sd, 0x71, tx_5v); adv76xx_s_detect_tx_5v_ctrl(sd); if (handled) *handled = true; @@ -2102,46 +2150,67 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /*********** avi info frame CEA-861-E **************/ -static void print_avi_infoframe(struct v4l2_subdev *sd) +static const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = { + { "AVI", 0x01, 0xe0, 0x00 }, + { "Audio", 0x02, 0xe3, 0x1c }, + { "SDP", 0x04, 0xe6, 0x2a }, + { "Vendor", 0x10, 0xec, 0x54 } +}; + +static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, + union hdmi_infoframe *frame) { + uint8_t buffer[32]; + u8 len; int i; - u8 buf[14]; - u8 avi_len; - u8 avi_ver; - if (!is_hdmi(sd)) { - v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); - return; + if (!(io_read(sd, 0x60) & adv76xx_cri[index].present_mask)) { + v4l2_info(sd, "%s infoframe not received\n", + adv76xx_cri[index].desc); + return -ENOENT; } - if (!(io_read(sd, 0x60) & 0x01)) { - v4l2_info(sd, "AVI infoframe not received\n"); - return; + + for (i = 0; i < 3; i++) + buffer[i] = infoframe_read(sd, + adv76xx_cri[index].head_addr + i); + + len = buffer[2] + 1; + + if (len + 3 > sizeof(buffer)) { + v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, + adv76xx_cri[index].desc, len); + return -ENOENT; } - if (io_read(sd, 0x83) & 0x01) { - v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n"); - io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */ - if (io_read(sd, 0x83) & 0x01) { - v4l2_info(sd, "AVI infoframe checksum error still present\n"); - io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */ - } + for (i = 0; i < len; i++) + buffer[i + 3] = infoframe_read(sd, + adv76xx_cri[index].payload_addr + i); + + if (hdmi_infoframe_unpack(frame, buffer) < 0) { + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, + adv76xx_cri[index].desc); + return -ENOENT; } + return 0; +} - avi_len = infoframe_read(sd, 0xe2); - avi_ver = infoframe_read(sd, 0xe1); - v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", - avi_ver, avi_len); +static void adv76xx_log_infoframes(struct v4l2_subdev *sd) +{ + int i; - if (avi_ver != 0x02) + if (!is_hdmi(sd)) { + v4l2_info(sd, "receive DVI-D signal, no infoframes\n"); return; + } - for (i = 0; i < 14; i++) - buf[i] = infoframe_read(sd, i); + for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) { + union hdmi_infoframe frame; + struct i2c_client *client = v4l2_get_subdevdata(sd); - v4l2_info(sd, - "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); + if (adv76xx_read_infoframe(sd, i, &frame)) + return; + hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); + } } static int adv76xx_log_status(struct v4l2_subdev *sd) @@ -2168,6 +2237,14 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "automatic" }; + static const char * const hdmi_color_space_txt[16] = { + "RGB limited range (16-235)", "RGB full range (0-255)", + "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", + "xvYCC Bt.601", "xvYCC Bt.709", + "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", + "sYCC", "Adobe YCC 601", "AdobeRGB", "invalid", "invalid", + "invalid", "invalid", "invalid" + }; static const char * const rgb_quantization_range_txt[] = { "Automatic", "RGB limited range (16-235)", @@ -2235,11 +2312,12 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) rgb_quantization_range_txt[state->rgb_quantization_range]); v4l2_info(sd, "Input color space: %s\n", input_color_space_txt[reg_io_0x02 >> 4]); - v4l2_info(sd, "Output color space: %s %s, saturator %s\n", + v4l2_info(sd, "Output color space: %s %s, saturator %s, alt-gamma %s\n", (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", - ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? - "enabled" : "disabled"); + (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ? + "enabled" : "disabled", + (reg_io_0x02 & 0x08) ? "enabled" : "disabled"); v4l2_info(sd, "Color space conversion: %s\n", csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]); @@ -2276,13 +2354,28 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "AV Mute: %s\n", (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[(hdmi_read(sd, 0x0b) & 0x60) >> 5]); + v4l2_info(sd, "HDMI colorspace: %s\n", hdmi_color_space_txt[hdmi_read(sd, 0x53) & 0xf]); - print_avi_infoframe(sd); + adv76xx_log_infoframes(sd); } return 0; } +static int adv76xx_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { @@ -2292,6 +2385,8 @@ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { static const struct v4l2_subdev_core_ops adv76xx_core_ops = { .log_status = adv76xx_log_status, .interrupt_service_routine = adv76xx_isr, + .subscribe_event = adv76xx_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv76xx_g_register, .s_register = adv76xx_s_register, @@ -2446,6 +2541,11 @@ static void adv7611_setup_irqs(struct v4l2_subdev *sd) io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ } +static void adv7612_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd0); /* disable INT2 */ +} + static void adv76xx_unregister_clients(struct adv76xx_state *state) { unsigned int i; @@ -2533,6 +2633,19 @@ static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = { { ADV76XX_REG_SEQ_TERM, 0 }, }; +static const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi[] = { + { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 }, + { ADV76XX_REG_SEQ_TERM, 0 }, +}; + static const struct adv76xx_chip_info adv76xx_chip_info[] = { [ADV7604] = { .type = ADV7604, @@ -2567,6 +2680,18 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_TEST) | BIT(ADV76XX_PAGE_CP) | BIT(ADV7604_PAGE_VDP), + .linewidth_mask = 0xfff, + .field0_height_mask = 0xfff, + .field1_height_mask = 0xfff, + .hfrontporch_mask = 0x3ff, + .hsync_mask = 0x3ff, + .hbackporch_mask = 0x3ff, + .field0_vfrontporch_mask = 0x1fff, + .field0_vsync_mask = 0x1fff, + .field0_vbackporch_mask = 0x1fff, + .field1_vfrontporch_mask = 0x1fff, + .field1_vsync_mask = 0x1fff, + .field1_vbackporch_mask = 0x1fff, }, [ADV7611] = { .type = ADV7611, @@ -2596,18 +2721,73 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), + .linewidth_mask = 0x1fff, + .field0_height_mask = 0x1fff, + .field1_height_mask = 0x1fff, + .hfrontporch_mask = 0x1fff, + .hsync_mask = 0x1fff, + .hbackporch_mask = 0x1fff, + .field0_vfrontporch_mask = 0x3fff, + .field0_vsync_mask = 0x3fff, + .field0_vbackporch_mask = 0x3fff, + .field1_vfrontporch_mask = 0x3fff, + .field1_vsync_mask = 0x3fff, + .field1_vbackporch_mask = 0x3fff, + }, + [ADV7612] = { + .type = ADV7612, + .has_afe = false, + .max_port = ADV76XX_PAD_HDMI_PORT_A, /* B not supported */ + .num_dv_ports = 1, /* normally 2 */ + .edid_enable_reg = 0x74, + .edid_status_reg = 0x76, + .lcf_reg = 0xa3, + .tdms_lock_mask = 0x43, + .cable_det_mask = 0x01, + .fmt_change_digital_mask = 0x03, + .cp_csc = 0xf4, + .formats = adv7612_formats, + .nformats = ARRAY_SIZE(adv7612_formats), + .set_termination = adv7611_set_termination, + .setup_irqs = adv7612_setup_irqs, + .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, + .read_cable_det = adv7612_read_cable_det, + .recommended_settings = { + [1] = adv7612_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [1] = ARRAY_SIZE(adv7612_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) | + BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | + BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | + BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), + .linewidth_mask = 0x1fff, + .field0_height_mask = 0x1fff, + .field1_height_mask = 0x1fff, + .hfrontporch_mask = 0x1fff, + .hsync_mask = 0x1fff, + .hbackporch_mask = 0x1fff, + .field0_vfrontporch_mask = 0x3fff, + .field0_vsync_mask = 0x3fff, + .field0_vbackporch_mask = 0x3fff, + .field1_vfrontporch_mask = 0x3fff, + .field1_vsync_mask = 0x3fff, + .field1_vbackporch_mask = 0x3fff, }, }; -static struct i2c_device_id adv76xx_i2c_id[] = { +static const struct i2c_device_id adv76xx_i2c_id[] = { { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); -static struct of_device_id adv76xx_of_id[] __maybe_unused = { +static const struct of_device_id adv76xx_of_id[] __maybe_unused = { { .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] }, + { .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(of, adv76xx_of_id); @@ -2618,6 +2798,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) struct device_node *endpoint; struct device_node *np; unsigned int flags; + u32 v; np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node; @@ -2627,6 +2808,12 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return -EINVAL; v4l2_of_parse_endpoint(endpoint, &bus_cfg); + + if (!of_property_read_u32(endpoint, "default-input", &v)) + state->pdata.default_input = v; + else + state->pdata.default_input = -1; + of_node_put(endpoint); flags = bus_cfg.bus.parallel.flags; @@ -2665,7 +2852,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; state->pdata.disable_cable_det_rst = 0; - state->pdata.default_input = -1; state->pdata.blank_data = 1; state->pdata.alt_data_sat = 1; state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; @@ -2674,6 +2860,148 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return 0; } +static const struct regmap_config adv76xx_regmap_cnf[] = { + { + .name = "io", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "avlink", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "cec", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "infoframe", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "esdp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "epp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "afe", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "rep", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "edid", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + + { + .name = "hdmi", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "test", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "cp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "vdp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, +}; + +static int configure_regmap(struct adv76xx_state *state, int region) +{ + int err; + + if (!state->i2c_clients[region]) + return -ENODEV; + + state->regmap[region] = + devm_regmap_init_i2c(state->i2c_clients[region], + &adv76xx_regmap_cnf[region]); + + if (IS_ERR(state->regmap[region])) { + err = PTR_ERR(state->regmap[region]); + v4l_err(state->i2c_clients[region], + "Error initializing regmap %d with error %d\n", + region, err); + return -EINVAL; + } + + return 0; +} + +static int configure_regmaps(struct adv76xx_state *state) +{ + int i, err; + + for (i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX; i++) { + err = configure_regmap(state, i); + if (err && (err != -ENODEV)) + return err; + } + return 0; +} + static int adv76xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2683,7 +3011,7 @@ static int adv76xx_probe(struct i2c_client *client, struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; unsigned int i; - u16 val; + unsigned int val, val2; int err; /* Check if the adapter supports the needed features */ @@ -2745,28 +3073,59 @@ static int adv76xx_probe(struct i2c_client *client, snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + + /* Configure IO Regmap region */ + err = configure_regmap(state, ADV76XX_PAGE_IO); + + if (err) { + v4l2_err(sd, "Error configuring IO regmap region\n"); + return -ENODEV; + } /* * Verify that the chip is present. On ADV7604 the RD_INFO register only * identifies the revision, while on ADV7611 it identifies the model as * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. */ - if (state->info->type == ADV7604) { - val = adv_smbus_read_byte_data_check(client, 0xfb, false); + switch (state->info->type) { + case ADV7604: + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } if (val != 0x68) { - v4l2_info(sd, "not an adv7604 on address 0x%x\n", + v4l2_err(sd, "not an adv7604 on address 0x%x\n", client->addr << 1); return -ENODEV; } - } else { - val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8) - | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0); - if (val != 0x2051) { - v4l2_info(sd, "not an adv7611 on address 0x%x\n", + break; + case ADV7611: + case ADV7612: + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], + 0xea, + &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } + val2 = val << 8; + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], + 0xeb, + &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } + val |= val2; + if ((state->info->type == ADV7611 && val != 0x2051) || + (state->info->type == ADV7612 && val != 0x2041)) { + v4l2_err(sd, "not an adv761x on address 0x%x\n", client->addr << 1); return -ENODEV; } + break; } /* control handlers */ @@ -2853,6 +3212,11 @@ static int adv76xx_probe(struct i2c_client *client, if (err) goto err_work_queues; + /* Configure regmaps */ + err = configure_regmaps(state); + if (err) + goto err_entity; + err = adv76xx_core_init(sd); if (err) goto err_entity; @@ -2897,7 +3261,6 @@ static int adv76xx_remove(struct i2c_client *client) static struct i2c_driver adv76xx_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7604", .of_match_table = of_match_ptr(adv76xx_of_id), }, diff --git a/kernel/drivers/media/i2c/adv7842.c b/kernel/drivers/media/i2c/adv7842.c index b5a37fe10..b7269b8f0 100644 --- a/kernel/drivers/media/i2c/adv7842.c +++ b/kernel/drivers/media/i2c/adv7842.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,28 @@ MODULE_LICENSE("GPL"); /* ADV7842 system clock frequency */ #define ADV7842_fsc (28636360) +#define ADV7842_RGB_OUT (1 << 1) + +#define ADV7842_OP_FORMAT_SEL_8BIT (0 << 0) +#define ADV7842_OP_FORMAT_SEL_10BIT (1 << 0) +#define ADV7842_OP_FORMAT_SEL_12BIT (2 << 0) + +#define ADV7842_OP_MODE_SEL_SDR_422 (0 << 5) +#define ADV7842_OP_MODE_SEL_DDR_422 (1 << 5) +#define ADV7842_OP_MODE_SEL_SDR_444 (2 << 5) +#define ADV7842_OP_MODE_SEL_DDR_444 (3 << 5) +#define ADV7842_OP_MODE_SEL_SDR_422_2X (4 << 5) +#define ADV7842_OP_MODE_SEL_ADI_CM (5 << 5) + +#define ADV7842_OP_CH_SEL_GBR (0 << 5) +#define ADV7842_OP_CH_SEL_GRB (1 << 5) +#define ADV7842_OP_CH_SEL_BGR (2 << 5) +#define ADV7842_OP_CH_SEL_RGB (3 << 5) +#define ADV7842_OP_CH_SEL_BRG (4 << 5) +#define ADV7842_OP_CH_SEL_RBG (5 << 5) + +#define ADV7842_OP_SWAP_CB_CR (1 << 0) + /* ********************************************************************** * @@ -64,6 +87,14 @@ MODULE_LICENSE("GPL"); ********************************************************************** */ +struct adv7842_format_info { + u32 code; + u8 op_ch_sel; + bool rgb_out; + bool swap_cb_cr; + u8 op_format_sel; +}; + struct adv7842_state { struct adv7842_platform_data pdata; struct v4l2_subdev sd; @@ -72,6 +103,9 @@ struct adv7842_state { enum adv7842_mode mode; struct v4l2_dv_timings timings; enum adv7842_vid_std_select vid_std_select; + + const struct adv7842_format_info *format; + v4l2_std_id norm; struct { u8 edid[256]; @@ -209,6 +243,11 @@ static const struct adv7842_video_standards adv7842_prim_mode_hdmi_gr[] = { { }, }; +static const struct v4l2_event adv7842_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + /* ----------------------------------------------------------------------- */ static inline struct adv7842_state *to_state(struct v4l2_subdev *sd) @@ -221,11 +260,21 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd; } +static inline unsigned hblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_WIDTH(t); +} + static inline unsigned htotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_WIDTH(t); } +static inline unsigned vblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_HEIGHT(t); +} + static inline unsigned vtotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_HEIGHT(t); @@ -335,6 +384,12 @@ static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 va return io_write(sd, reg, (io_read(sd, reg) & mask) | val); } +static inline int io_write_clr_set(struct v4l2_subdev *sd, + u8 reg, u8 mask, u8 val) +{ + return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); +} + static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv7842_state *state = to_state(sd); @@ -535,6 +590,64 @@ static void main_reset(struct v4l2_subdev *sd) mdelay(5); } +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +static const struct adv7842_format_info adv7842_formats[] = { + { MEDIA_BUS_FMT_RGB888_1X24, ADV7842_OP_CH_SEL_RGB, true, false, + ADV7842_OP_MODE_SEL_SDR_444 | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_2X8, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_2X8, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV10_2X10, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YVYU10_2X10, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YUYV12_2X12, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_2X12, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_UYVY8_1X16, ADV7842_OP_CH_SEL_RBG, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_VYUY8_1X16, ADV7842_OP_CH_SEL_RBG, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_1X16, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_1X16, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_UYVY10_1X20, ADV7842_OP_CH_SEL_RBG, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_VYUY10_1X20, ADV7842_OP_CH_SEL_RBG, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YUYV10_1X20, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_YVYU10_1X20, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT }, + { MEDIA_BUS_FMT_UYVY12_1X24, ADV7842_OP_CH_SEL_RBG, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_VYUY12_1X24, ADV7842_OP_CH_SEL_RBG, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YUYV12_1X24, ADV7842_OP_CH_SEL_RGB, false, false, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT }, + { MEDIA_BUS_FMT_YVYU12_1X24, ADV7842_OP_CH_SEL_RGB, false, true, + ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7842_format_info * +adv7842_format_info(struct adv7842_state *state, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adv7842_formats); ++i) { + if (adv7842_formats[i].code == code) + return &adv7842_formats[i]; + } + + return NULL; +} + /* ----------------------------------------------------------------------- */ static inline bool is_analog_input(struct v4l2_subdev *sd) @@ -1330,15 +1443,15 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, } } - if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - timings)) + false, timings)) return 0; if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), - state->aspect_ratio, timings)) + false, state->aspect_ratio, timings)) return 0; v4l2_dbg(2, debug, sd, @@ -1440,9 +1553,11 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, } bt->interlaced = stdi.interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + bt->standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT; if (is_digital_input(sd)) { - uint32_t freq; + u32 freq; timings->type = V4L2_DV_BT_656_1120; @@ -1478,6 +1593,10 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, hdmi_read(sd, 0x31)) / 2; bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + hdmi_read(sd, 0x35)) / 2; + } else { + bt->il_vfrontporch = 0; + bt->il_vsync = 0; + bt->il_vbackporch = 0; } adv7842_fill_optional_dv_timings_fields(sd, timings); } else { @@ -1862,50 +1981,154 @@ static int adv7842_s_routing(struct v4l2_subdev *sd, select_input(sd, state->vid_std_select); enable_input(sd); - v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + v4l2_subdev_notify_event(sd, &adv7842_ev_fmt); return 0; } -static int adv7842_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int adv7842_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + if (code->index >= ARRAY_SIZE(adv7842_formats)) return -EINVAL; - /* Good enough for now */ - *code = MEDIA_BUS_FMT_FIXED; + code->code = adv7842_formats[code->index].code; return 0; } -static int adv7842_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static void adv7842_fill_format(struct adv7842_state *state, + struct v4l2_mbus_framefmt *format) +{ + memset(format, 0, sizeof(*format)); + + format->width = state->timings.bt.width; + format->height = state->timings.bt.height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) + format->colorspace = (state->timings.bt.height <= 576) ? + V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; +} + +/* + * Compute the op_ch_sel value required to obtain on the bus the component order + * corresponding to the selected format taking into account bus reordering + * applied by the board at the output of the device. + * + * The following table gives the op_ch_value from the format component order + * (expressed as op_ch_sel value in column) and the bus reordering (expressed as + * adv7842_bus_order value in row). + * + * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5) + * ----------+------------------------------------------------- + * RGB (NOP) | GBR GRB BGR RGB BRG RBG + * GRB (1-2) | BGR RGB GBR GRB RBG BRG + * RBG (2-3) | GRB GBR BRG RBG BGR RGB + * BGR (1-3) | RBG BRG RGB BGR GRB GBR + * BRG (ROR) | BRG RBG GRB GBR RGB BGR + * GBR (ROL) | RGB BGR RBG BRG GBR GRB + */ +static unsigned int adv7842_op_ch_sel(struct adv7842_state *state) +{ +#define _SEL(a, b, c, d, e, f) { \ + ADV7842_OP_CH_SEL_##a, ADV7842_OP_CH_SEL_##b, ADV7842_OP_CH_SEL_##c, \ + ADV7842_OP_CH_SEL_##d, ADV7842_OP_CH_SEL_##e, ADV7842_OP_CH_SEL_##f } +#define _BUS(x) [ADV7842_BUS_ORDER_##x] + + static const unsigned int op_ch_sel[6][6] = { + _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG), + _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG), + _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB), + _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR), + _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR), + _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB), + }; + + return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; +} + +static void adv7842_setup_format(struct adv7842_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + + io_write_clr_set(sd, 0x02, 0x02, + state->format->rgb_out ? ADV7842_RGB_OUT : 0); + io_write(sd, 0x03, state->format->op_format_sel | + state->pdata.op_format_mode_sel); + io_write_clr_set(sd, 0x04, 0xe0, adv7842_op_ch_sel(state)); + io_write_clr_set(sd, 0x05, 0x01, + state->format->swap_cb_cr ? ADV7842_OP_SWAP_CB_CR : 0); +} + +static int adv7842_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct adv7842_state *state = to_state(sd); - fmt->width = state->timings.bt.width; - fmt->height = state->timings.bt.height; - fmt->code = MEDIA_BUS_FMT_FIXED; - fmt->field = V4L2_FIELD_NONE; + if (format->pad != ADV7842_PAD_SOURCE) + return -EINVAL; if (state->mode == ADV7842_MODE_SDP) { /* SPD block */ - if (!(sdp_read(sd, 0x5A) & 0x01)) + if (!(sdp_read(sd, 0x5a) & 0x01)) return -EINVAL; - fmt->width = 720; + format->format.code = MEDIA_BUS_FMT_YUYV8_2X8; + format->format.width = 720; /* valid signal */ if (state->norm & V4L2_STD_525_60) - fmt->height = 480; + format->format.height = 480; else - fmt->height = 576; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + format->format.height = 576; + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } - fmt->colorspace = V4L2_COLORSPACE_SRGB; - if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { - fmt->colorspace = (state->timings.bt.height <= 576) ? - V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + adv7842_fill_format(state, &format->format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + format->format.code = fmt->code; + } else { + format->format.code = state->format->code; } + + return 0; +} + +static int adv7842_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct adv7842_state *state = to_state(sd); + const struct adv7842_format_info *info; + + if (format->pad != ADV7842_PAD_SOURCE) + return -EINVAL; + + if (state->mode == ADV7842_MODE_SDP) + return adv7842_get_format(sd, cfg, format); + + info = adv7842_format_info(state, format->format.code); + if (info == NULL) + info = adv7842_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); + + adv7842_fill_format(state, &format->format); + format->format.code = info->code; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + fmt->code = format->format.code; + } else { + state->format = info; + adv7842_setup_format(state); + } + return 0; } @@ -1991,7 +2214,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", __func__, fmt_change_cp, fmt_change_digital, fmt_change_sdp); - v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + v4l2_subdev_notify_event(sd, &adv7842_ev_fmt); if (handled) *handled = true; } @@ -2110,7 +2333,7 @@ struct adv7842_cfg_read_infoframe { static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infoframe *cri) { int i; - uint8_t buffer[32]; + u8 buffer[32]; union hdmi_infoframe frame; u8 len; struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -2183,7 +2406,7 @@ static const char * const prim_mode_txt[] = { static int adv7842_sdp_log_status(struct v4l2_subdev *sd) { /* SDP (Standard definition processor) block */ - uint8_t sdp_signal_detected = sdp_read(sd, 0x5A) & 0x01; + u8 sdp_signal_detected = sdp_read(sd, 0x5A) & 0x01; v4l2_info(sd, "Chip powered %s\n", no_power(sd) ? "off" : "on"); v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x\n", @@ -2227,10 +2450,10 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) /* CP block */ struct adv7842_state *state = to_state(sd); struct v4l2_dv_timings timings; - uint8_t reg_io_0x02 = io_read(sd, 0x02); - uint8_t reg_io_0x21 = io_read(sd, 0x21); - uint8_t reg_rep_0x77 = rep_read(sd, 0x77); - uint8_t reg_rep_0x7d = rep_read(sd, 0x7d); + u8 reg_io_0x02 = io_read(sd, 0x02); + u8 reg_io_0x21 = io_read(sd, 0x21); + u8 reg_rep_0x77 = rep_read(sd, 0x77); + u8 reg_rep_0x7d = rep_read(sd, 0x7d); bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01; bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01; bool audio_mute = io_read(sd, 0x65) & 0x40; @@ -2302,10 +2525,10 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) if (no_cp_signal(sd)) { v4l2_info(sd, "STDI: not locked\n"); } else { - uint32_t bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); - uint32_t lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); - uint32_t lcvs = cp_read(sd, 0xb3) >> 3; - uint32_t fcl = ((cp_read(sd, 0xb8) & 0x1f) << 8) | cp_read(sd, 0xb9); + u32 bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); + u32 lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + u32 lcvs = cp_read(sd, 0xb3) >> 3; + u32 fcl = ((cp_read(sd, 0xb8) & 0x1f) << 8) | cp_read(sd, 0xb9); char hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); char vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? @@ -2545,14 +2768,11 @@ static int adv7842_core_init(struct v4l2_subdev *sd) 0xf0 | pdata->alt_gamma << 3 | pdata->op_656_range << 2 | - pdata->rgb_out << 1 | pdata->alt_data_sat << 0); - io_write(sd, 0x03, pdata->op_format_sel); - io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | pdata->insert_av_codes << 2 | - pdata->replicate_av_codes << 1 | - pdata->invert_cbcr << 0); + pdata->replicate_av_codes << 1); + adv7842_setup_format(state); /* HDMI audio */ hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ @@ -2784,6 +3004,20 @@ static long adv7842_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return -ENOTTY; } +static int adv7842_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { @@ -2794,6 +3028,8 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = { .log_status = adv7842_log_status, .ioctl = adv7842_ioctl, .interrupt_service_routine = adv7842_isr, + .subscribe_event = adv7842_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7842_g_register, .s_register = adv7842_s_register, @@ -2809,13 +3045,12 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { .s_dv_timings = adv7842_s_dv_timings, .g_dv_timings = adv7842_g_dv_timings, .query_dv_timings = adv7842_query_dv_timings, - .enum_mbus_fmt = adv7842_enum_mbus_fmt, - .g_mbus_fmt = adv7842_g_mbus_fmt, - .try_mbus_fmt = adv7842_g_mbus_fmt, - .s_mbus_fmt = adv7842_g_mbus_fmt, }; static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { + .enum_mbus_code = adv7842_enum_mbus_code, + .get_fmt = adv7842_get_format, + .set_fmt = adv7842_set_format, .get_edid = adv7842_get_edid, .set_edid = adv7842_set_edid, .enum_dv_timings = adv7842_enum_dv_timings, @@ -2986,10 +3221,11 @@ static int adv7842_probe(struct i2c_client *client, /* platform data */ state->pdata = *pdata; state->timings = cea640x480; + state->format = adv7842_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8); sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; state->mode = pdata->mode; state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; @@ -3127,7 +3363,6 @@ MODULE_DEVICE_TABLE(i2c, adv7842_id); static struct i2c_driver adv7842_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7842", }, .probe = adv7842_probe, diff --git a/kernel/drivers/media/i2c/ak881x.c b/kernel/drivers/media/i2c/ak881x.c index 69aeaf397..d3b965ec3 100644 --- a/kernel/drivers/media/i2c/ak881x.c +++ b/kernel/drivers/media/i2c/ak881x.c @@ -93,12 +93,17 @@ static int ak881x_s_register(struct v4l2_subdev *sd, } #endif -static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ak881x_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ak881x *ak881x = to_ak881x(client); + if (format->pad) + return -EINVAL; + v4l_bound_align_image(&mf->width, 0, 720, 2, &mf->height, 0, ak881x->lines, 1, 0); mf->field = V4L2_FIELD_INTERLACED; @@ -108,23 +113,14 @@ static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (mf->field != V4L2_FIELD_INTERLACED || - mf->code != MEDIA_BUS_FMT_YUYV8_2X8) - return -EINVAL; - - return ak881x_try_g_mbus_fmt(sd, mf); -} - -static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ak881x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + if (code->pad || code->index) return -EINVAL; - *code = MEDIA_BUS_FMT_YUYV8_2X8; + code->code = MEDIA_BUS_FMT_YUYV8_2X8; return 0; } @@ -160,12 +156,12 @@ static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) } else if (std == V4L2_STD_PAL_60) { vp1 = 7; ak881x->lines = 480; - } else if (std && !(std & ~V4L2_STD_PAL)) { - vp1 = 0xf; - ak881x->lines = 576; - } else if (std && !(std & ~V4L2_STD_NTSC)) { + } else if (std & V4L2_STD_NTSC) { vp1 = 0; ak881x->lines = 480; + } else if (std & V4L2_STD_PAL) { + vp1 = 0xf; + ak881x->lines = 576; } else { /* No SECAM or PAL_N/Nc supported */ return -EINVAL; @@ -211,18 +207,21 @@ static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { }; static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { - .s_mbus_fmt = ak881x_s_mbus_fmt, - .g_mbus_fmt = ak881x_try_g_mbus_fmt, - .try_mbus_fmt = ak881x_try_g_mbus_fmt, .cropcap = ak881x_cropcap, - .enum_mbus_fmt = ak881x_enum_mbus_fmt, .s_std_output = ak881x_s_std_output, .s_stream = ak881x_s_stream, }; +static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = { + .enum_mbus_code = ak881x_enum_mbus_code, + .set_fmt = ak881x_fill_fmt, + .get_fmt = ak881x_fill_fmt, +}; + static struct v4l2_subdev_ops ak881x_subdev_ops = { .core = &ak881x_subdev_core_ops, .video = &ak881x_subdev_video_ops, + .pad = &ak881x_subdev_pad_ops, }; static int ak881x_probe(struct i2c_client *client, diff --git a/kernel/drivers/media/i2c/bt819.c b/kernel/drivers/media/i2c/bt819.c index 76b334a6a..e00e3104d 100644 --- a/kernel/drivers/media/i2c/bt819.c +++ b/kernel/drivers/media/i2c/bt819.c @@ -379,16 +379,6 @@ static const struct v4l2_ctrl_ops bt819_ctrl_ops = { .s_ctrl = bt819_s_ctrl, }; -static const struct v4l2_subdev_core_ops bt819_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - static const struct v4l2_subdev_video_ops bt819_video_ops = { .s_std = bt819_s_std, .s_routing = bt819_s_routing, @@ -398,7 +388,6 @@ static const struct v4l2_subdev_video_ops bt819_video_ops = { }; static const struct v4l2_subdev_ops bt819_ops = { - .core = &bt819_core_ops, .video = &bt819_video_ops, }; @@ -492,7 +481,6 @@ MODULE_DEVICE_TABLE(i2c, bt819_id); static struct i2c_driver bt819_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt819", }, .probe = bt819_probe, diff --git a/kernel/drivers/media/i2c/bt856.c b/kernel/drivers/media/i2c/bt856.c index 7fc163d02..48176591a 100644 --- a/kernel/drivers/media/i2c/bt856.c +++ b/kernel/drivers/media/i2c/bt856.c @@ -252,7 +252,6 @@ MODULE_DEVICE_TABLE(i2c, bt856_id); static struct i2c_driver bt856_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt856", }, .probe = bt856_probe, diff --git a/kernel/drivers/media/i2c/bt866.c b/kernel/drivers/media/i2c/bt866.c index a8bf10fc6..bbec70c88 100644 --- a/kernel/drivers/media/i2c/bt866.c +++ b/kernel/drivers/media/i2c/bt866.c @@ -218,7 +218,6 @@ MODULE_DEVICE_TABLE(i2c, bt866_id); static struct i2c_driver bt866_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt866", }, .probe = bt866_probe, diff --git a/kernel/drivers/media/i2c/cs5345.c b/kernel/drivers/media/i2c/cs5345.c index 34b76a9e7..c7de9790d 100644 --- a/kernel/drivers/media/i2c/cs5345.c +++ b/kernel/drivers/media/i2c/cs5345.c @@ -132,13 +132,6 @@ static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { static const struct v4l2_subdev_core_ops cs5345_core_ops = { .log_status = cs5345_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cs5345_g_register, .s_register = cs5345_s_register, @@ -218,7 +211,6 @@ MODULE_DEVICE_TABLE(i2c, cs5345_id); static struct i2c_driver cs5345_driver = { .driver = { - .owner = THIS_MODULE, .name = "cs5345", }, .probe = cs5345_probe, diff --git a/kernel/drivers/media/i2c/cs53l32a.c b/kernel/drivers/media/i2c/cs53l32a.c index 27400c16e..b7e87e386 100644 --- a/kernel/drivers/media/i2c/cs53l32a.c +++ b/kernel/drivers/media/i2c/cs53l32a.c @@ -228,7 +228,6 @@ MODULE_DEVICE_TABLE(i2c, cs53l32a_id); static struct i2c_driver cs53l32a_driver = { .driver = { - .owner = THIS_MODULE, .name = "cs53l32a", }, .probe = cs53l32a_probe, diff --git a/kernel/drivers/media/i2c/cx25840/cx25840-core.c b/kernel/drivers/media/i2c/cx25840/cx25840-core.c index bd4964477..fe6eb78b6 100644 --- a/kernel/drivers/media/i2c/cx25840/cx25840-core.c +++ b/kernel/drivers/media/i2c/cx25840/cx25840-core.c @@ -971,7 +971,7 @@ static void input_change(struct i2c_client *client) not used by any public broadcast network, force 6.5 MHz carrier to be interpreted as System DK, this avoids DK audio detection instability */ - cx25840_write(client, 0x80b, 0x00); + cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_SECAM) { /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); @@ -1366,14 +1366,17 @@ static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) /* ----------------------------------------------------------------------- */ -static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +static int cx25840_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); - if (fmt->code != MEDIA_BUS_FMT_FIXED) + if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) return -EINVAL; fmt->field = V4L2_FIELD_INTERLACED; @@ -1403,6 +1406,8 @@ static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt fmt->width, fmt->height); return -ERANGE; } + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); @@ -5068,7 +5073,6 @@ static const struct v4l2_subdev_video_ops cx25840_video_ops = { .s_std = cx25840_s_std, .g_std = cx25840_g_std, .s_routing = cx25840_s_video_routing, - .s_mbus_fmt = cx25840_s_mbus_fmt, .s_stream = cx25840_s_stream, .g_input_status = cx25840_g_input_status, }; @@ -5080,12 +5084,17 @@ static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { .g_sliced_fmt = cx25840_g_sliced_fmt, }; +static const struct v4l2_subdev_pad_ops cx25840_pad_ops = { + .set_fmt = cx25840_set_fmt, +}; + static const struct v4l2_subdev_ops cx25840_ops = { .core = &cx25840_core_ops, .tuner = &cx25840_tuner_ops, .audio = &cx25840_audio_ops, .video = &cx25840_video_ops, .vbi = &cx25840_vbi_ops, + .pad = &cx25840_pad_ops, .ir = &cx25840_ir_ops, }; @@ -5339,7 +5348,6 @@ MODULE_DEVICE_TABLE(i2c, cx25840_id); static struct i2c_driver cx25840_driver = { .driver = { - .owner = THIS_MODULE, .name = "cx25840", }, .probe = cx25840_probe, diff --git a/kernel/drivers/media/i2c/ks0127.c b/kernel/drivers/media/i2c/ks0127.c index 25b81bc58..77551baab 100644 --- a/kernel/drivers/media/i2c/ks0127.c +++ b/kernel/drivers/media/i2c/ks0127.c @@ -708,7 +708,6 @@ MODULE_DEVICE_TABLE(i2c, ks0127_id); static struct i2c_driver ks0127_driver = { .driver = { - .owner = THIS_MODULE, .name = "ks0127", }, .probe = ks0127_probe, diff --git a/kernel/drivers/media/i2c/m52790.c b/kernel/drivers/media/i2c/m52790.c index bf4763587..77eb07eb6 100644 --- a/kernel/drivers/media/i2c/m52790.c +++ b/kernel/drivers/media/i2c/m52790.c @@ -185,7 +185,6 @@ MODULE_DEVICE_TABLE(i2c, m52790_id); static struct i2c_driver m52790_driver = { .driver = { - .owner = THIS_MODULE, .name = "m52790", }, .probe = m52790_probe, diff --git a/kernel/drivers/media/i2c/ml86v7667.c b/kernel/drivers/media/i2c/ml86v7667.c index d7307862c..38a20fe18 100644 --- a/kernel/drivers/media/i2c/ml86v7667.c +++ b/kernel/drivers/media/i2c/ml86v7667.c @@ -191,21 +191,27 @@ static int ml86v7667_g_input_status(struct v4l2_subdev *sd, u32 *status) return 0; } -static int ml86v7667_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ml86v7667_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index > 0) + if (code->pad || code->index > 0) return -EINVAL; - *code = MEDIA_BUS_FMT_YUYV8_2X8; + code->code = MEDIA_BUS_FMT_YUYV8_2X8; return 0; } -static int ml86v7667_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int ml86v7667_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct ml86v7667_priv *priv = to_ml86v7667(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + if (format->pad) + return -EINVAL; fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -227,6 +233,15 @@ static int ml86v7667_g_mbus_config(struct v4l2_subdev *sd, return 0; } +static int ml86v7667_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct ml86v7667_priv *priv = to_ml86v7667(sd); + + *std = priv->std; + + return 0; +} + static int ml86v7667_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct ml86v7667_priv *priv = to_ml86v7667(sd); @@ -276,16 +291,19 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = { }; static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { + .g_std = ml86v7667_g_std, .s_std = ml86v7667_s_std, .querystd = ml86v7667_querystd, .g_input_status = ml86v7667_g_input_status, - .enum_mbus_fmt = ml86v7667_enum_mbus_fmt, - .try_mbus_fmt = ml86v7667_mbus_fmt, - .g_mbus_fmt = ml86v7667_mbus_fmt, - .s_mbus_fmt = ml86v7667_mbus_fmt, .g_mbus_config = ml86v7667_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = { + .enum_mbus_code = ml86v7667_enum_mbus_code, + .get_fmt = ml86v7667_fill_fmt, + .set_fmt = ml86v7667_fill_fmt, +}; + static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ml86v7667_g_register, @@ -296,6 +314,7 @@ static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { static struct v4l2_subdev_ops ml86v7667_subdev_ops = { .core = &ml86v7667_subdev_core_ops, .video = &ml86v7667_subdev_video_ops, + .pad = &ml86v7667_subdev_pad_ops, }; static int ml86v7667_init(struct ml86v7667_priv *priv) @@ -418,7 +437,6 @@ MODULE_DEVICE_TABLE(i2c, ml86v7667_id); static struct i2c_driver ml86v7667_i2c_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, }, .probe = ml86v7667_probe, .remove = ml86v7667_remove, diff --git a/kernel/drivers/media/i2c/msp3400-driver.c b/kernel/drivers/media/i2c/msp3400-driver.c index dcc68ec71..bdb94000b 100644 --- a/kernel/drivers/media/i2c/msp3400-driver.c +++ b/kernel/drivers/media/i2c/msp3400-driver.c @@ -894,7 +894,6 @@ MODULE_DEVICE_TABLE(i2c, msp_id); static struct i2c_driver msp_driver = { .driver = { - .owner = THIS_MODULE, .name = "msp3400", .pm = &msp3400_pm_ops, }, diff --git a/kernel/drivers/media/i2c/mt9v011.c b/kernel/drivers/media/i2c/mt9v011.c index a10f7f8f0..a4a5c39b5 100644 --- a/kernel/drivers/media/i2c/mt9v011.c +++ b/kernel/drivers/media/i2c/mt9v011.c @@ -324,19 +324,25 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) return 0; } -static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) +static int mt9v011_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index > 0) + if (code->pad || code->index > 0) return -EINVAL; - *code = MEDIA_BUS_FMT_SGRBG8_1X8; + code->code = MEDIA_BUS_FMT_SGRBG8_1X8; return 0; } -static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +static int mt9v011_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { - if (fmt->code != MEDIA_BUS_FMT_SGRBG8_1X8) + struct v4l2_mbus_framefmt *fmt = &format->format; + struct mt9v011 *core = to_mt9v011(sd); + + if (format->pad || fmt->code != MEDIA_BUS_FMT_SGRBG8_1X8) return -EINVAL; v4l_bound_align_image(&fmt->width, 48, 639, 1, @@ -344,6 +350,15 @@ static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefm fmt->field = V4L2_FIELD_NONE; fmt->colorspace = V4L2_COLORSPACE_SRGB; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + core->width = fmt->width; + core->height = fmt->height; + + set_res(sd); + } else { + cfg->try_fmt = *fmt; + } + return 0; } @@ -385,23 +400,6 @@ static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) return 0; } -static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) -{ - struct mt9v011 *core = to_mt9v011(sd); - int rc; - - rc = mt9v011_try_mbus_fmt(sd, fmt); - if (rc < 0) - return -EINVAL; - - core->width = fmt->width; - core->height = fmt->height; - - set_res(sd); - - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v011_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) @@ -469,16 +467,19 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = { }; static const struct v4l2_subdev_video_ops mt9v011_video_ops = { - .enum_mbus_fmt = mt9v011_enum_mbus_fmt, - .try_mbus_fmt = mt9v011_try_mbus_fmt, - .s_mbus_fmt = mt9v011_s_mbus_fmt, .g_parm = mt9v011_g_parm, .s_parm = mt9v011_s_parm, }; +static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = { + .enum_mbus_code = mt9v011_enum_mbus_code, + .set_fmt = mt9v011_set_fmt, +}; + static const struct v4l2_subdev_ops mt9v011_ops = { .core = &mt9v011_core_ops, .video = &mt9v011_video_ops, + .pad = &mt9v011_pad_ops, }; @@ -582,7 +583,6 @@ MODULE_DEVICE_TABLE(i2c, mt9v011_id); static struct i2c_driver mt9v011_driver = { .driver = { - .owner = THIS_MODULE, .name = "mt9v011", }, .probe = mt9v011_probe, diff --git a/kernel/drivers/media/i2c/mt9v032.c b/kernel/drivers/media/i2c/mt9v032.c index 977f4006e..a68ce94ee 100644 --- a/kernel/drivers/media/i2c/mt9v032.c +++ b/kernel/drivers/media/i2c/mt9v032.c @@ -882,7 +882,7 @@ static const struct regmap_config mt9v032_regmap_config = { static struct mt9v032_platform_data * mt9v032_get_pdata(struct i2c_client *client) { - struct mt9v032_platform_data *pdata; + struct mt9v032_platform_data *pdata = NULL; struct v4l2_of_endpoint endpoint; struct device_node *np; struct property *prop; diff --git a/kernel/drivers/media/i2c/ov2659.c b/kernel/drivers/media/i2c/ov2659.c index edebd1142..49109f4f5 100644 --- a/kernel/drivers/media/i2c/ov2659.c +++ b/kernel/drivers/media/i2c/ov2659.c @@ -909,7 +909,6 @@ static void ov2659_pll_calc_params(struct ov2659 *ov2659) u8 ctrl1_reg = 0, ctrl2_reg = 0, ctrl3_reg = 0; struct i2c_client *client = ov2659->client; unsigned int desired = pdata->link_frequency; - u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; u32 prediv, postdiv, mult; u32 bestdelta = -1; u32 delta, actual; @@ -929,9 +928,6 @@ static void ov2659_pll_calc_params(struct ov2659 *ov2659) if ((delta < bestdelta) || (bestdelta == -1)) { bestdelta = delta; - s_mult = mult; - s_prediv = prediv; - s_postdiv = postdiv; ctrl1_reg = ctrl1[i].reg; ctrl2_reg = mult; ctrl3_reg = ctrl3[j].reg; @@ -1046,16 +1042,21 @@ static int ov2659_get_fmt(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov2659 *ov2659 = to_ov2659(sd); - struct v4l2_mbus_framefmt *mf; dev_dbg(&client->dev, "ov2659_get_fmt\n"); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *mf; + mf = v4l2_subdev_get_try_format(sd, cfg, 0); mutex_lock(&ov2659->lock); fmt->format = *mf; mutex_unlock(&ov2659->lock); return 0; +#else + return -ENOTTY; +#endif } mutex_lock(&ov2659->lock); @@ -1102,7 +1103,7 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned int index = ARRAY_SIZE(ov2659_formats); + int index = ARRAY_SIZE(ov2659_formats); struct v4l2_mbus_framefmt *mf = &fmt->format; const struct ov2659_framesize *size = NULL; struct ov2659 *ov2659 = to_ov2659(sd); @@ -1126,8 +1127,12 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mutex_lock(&ov2659->lock); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; +#else + return -ENOTTY; +#endif } else { s64 val; @@ -1257,6 +1262,7 @@ static const char * const ov2659_test_pattern_menu[] = { * V4L2 subdev internal operations */ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1269,6 +1275,7 @@ static int ov2659_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } +#endif static const struct v4l2_subdev_core_ops ov2659_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, @@ -1287,6 +1294,7 @@ static const struct v4l2_subdev_pad_ops ov2659_subdev_pad_ops = { .set_fmt = ov2659_set_fmt, }; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static const struct v4l2_subdev_ops ov2659_subdev_ops = { .core = &ov2659_subdev_core_ops, .video = &ov2659_subdev_video_ops, @@ -1296,6 +1304,7 @@ static const struct v4l2_subdev_ops ov2659_subdev_ops = { static const struct v4l2_subdev_internal_ops ov2659_subdev_internal_ops = { .open = ov2659_open, }; +#endif static int ov2659_detect(struct v4l2_subdev *sd) { @@ -1340,8 +1349,8 @@ static struct ov2659_platform_data * ov2659_get_pdata(struct i2c_client *client) { struct ov2659_platform_data *pdata; + struct v4l2_of_endpoint *bus_cfg; struct device_node *endpoint; - int ret; if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) return client->dev.platform_data; @@ -1350,18 +1359,27 @@ ov2659_get_pdata(struct i2c_client *client) if (!endpoint) return NULL; + bus_cfg = v4l2_of_alloc_parse_endpoint(endpoint); + if (IS_ERR(bus_cfg)) { + pdata = NULL; + goto done; + } + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) goto done; - ret = of_property_read_u64(endpoint, "link-frequencies", - &pdata->link_frequency); - if (ret) { - dev_err(&client->dev, "link-frequencies property not found\n"); + if (!bus_cfg->nr_of_link_frequencies) { + dev_err(&client->dev, + "link-frequencies property not found or too many\n"); pdata = NULL; + goto done; } + pdata->link_frequency = bus_cfg->link_frequencies[0]; + done: + v4l2_of_free_endpoint(bus_cfg); of_node_put(endpoint); return pdata; } @@ -1417,11 +1435,13 @@ static int ov2659_probe(struct i2c_client *client, sd = &ov2659->sd; client->flags |= I2C_CLIENT_SCCB; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API v4l2_i2c_subdev_init(sd, client, &ov2659_subdev_ops); sd->internal_ops = &ov2659_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; +#endif #if defined(CONFIG_MEDIA_CONTROLLER) ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/kernel/drivers/media/i2c/ov7640.c b/kernel/drivers/media/i2c/ov7640.c index faa64baf0..b8961df5a 100644 --- a/kernel/drivers/media/i2c/ov7640.c +++ b/kernel/drivers/media/i2c/ov7640.c @@ -94,7 +94,6 @@ MODULE_DEVICE_TABLE(i2c, ov7640_id); static struct i2c_driver ov7640_driver = { .driver = { - .owner = THIS_MODULE, .name = "ov7640", }, .probe = ov7640_probe, diff --git a/kernel/drivers/media/i2c/ov7670.c b/kernel/drivers/media/i2c/ov7670.c index b9847527e..e1b5dc84c 100644 --- a/kernel/drivers/media/i2c/ov7670.c +++ b/kernel/drivers/media/i2c/ov7670.c @@ -639,7 +639,7 @@ static struct ov7670_format_struct { } ov7670_formats[] = { { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .regs = ov7670_fmt_yuv422, .cmatrix = { 128, -128, 0, -34, -94, 128 }, }, @@ -899,13 +899,14 @@ static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, } -static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) +static int ov7670_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= N_OV7670_FMTS) + if (code->pad || code->index >= N_OV7670_FMTS) return -EINVAL; - *code = ov7670_formats[index].mbus_code; + code->code = ov7670_formats[code->index].mbus_code; return 0; } @@ -970,17 +971,12 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, return 0; } -static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); -} - /* * Set a format. */ -static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int ov7670_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct ov7670_format_struct *ovfmt; struct ov7670_win_size *wsize; @@ -988,7 +984,18 @@ static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, unsigned char com7; int ret; - ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + if (format->pad) + return -EINVAL; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); + if (ret) + return ret; + cfg->try_fmt = format->format; + return 0; + } + + ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize); if (ret) return ret; @@ -1073,10 +1080,33 @@ static int ov7670_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { + struct ov7670_info *info = to_state(sd); + unsigned int n_win_sizes = info->devtype->n_win_sizes; + int i; + if (fie->pad) return -EINVAL; if (fie->index >= ARRAY_SIZE(ov7670_frame_rates)) return -EINVAL; + + /* + * Check if the width/height is valid. + * + * If a minimum width/height was requested, filter out the capture + * windows that fall outside that. + */ + for (i = 0; i < n_win_sizes; i++) { + struct ov7670_win_size *win = &info->devtype->win_sizes[i]; + + if (info->min_width && win->width < info->min_width) + continue; + if (info->min_height && win->height < info->min_height) + continue; + if (fie->width == win->width && fie->height == win->height) + break; + } + if (i == n_win_sizes) + return -EINVAL; fie->interval.numerator = 1; fie->interval.denominator = ov7670_frame_rates[fie->index]; return 0; @@ -1362,7 +1392,7 @@ static int ov7670_s_exp(struct v4l2_subdev *sd, int value) unsigned char com1, com8, aech, aechh; ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_COM8, &com8); + ov7670_read(sd, REG_COM8, &com8) + ov7670_read(sd, REG_AECHH, &aechh); if (ret) return ret; @@ -1485,9 +1515,6 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { }; static const struct v4l2_subdev_video_ops ov7670_video_ops = { - .enum_mbus_fmt = ov7670_enum_mbus_fmt, - .try_mbus_fmt = ov7670_try_mbus_fmt, - .s_mbus_fmt = ov7670_s_mbus_fmt, .s_parm = ov7670_s_parm, .g_parm = ov7670_g_parm, }; @@ -1495,6 +1522,8 @@ static const struct v4l2_subdev_video_ops ov7670_video_ops = { static const struct v4l2_subdev_pad_ops ov7670_pad_ops = { .enum_frame_interval = ov7670_enum_frame_interval, .enum_frame_size = ov7670_enum_frame_size, + .enum_mbus_code = ov7670_enum_mbus_code, + .set_fmt = ov7670_set_fmt, }; static const struct v4l2_subdev_ops ov7670_ops = { @@ -1645,7 +1674,6 @@ MODULE_DEVICE_TABLE(i2c, ov7670_id); static struct i2c_driver ov7670_driver = { .driver = { - .owner = THIS_MODULE, .name = "ov7670", }, .probe = ov7670_probe, diff --git a/kernel/drivers/media/i2c/ov9650.c b/kernel/drivers/media/i2c/ov9650.c index 2bc473385..1ee6a5527 100644 --- a/kernel/drivers/media/i2c/ov9650.c +++ b/kernel/drivers/media/i2c/ov9650.c @@ -1133,7 +1133,7 @@ static int __ov965x_set_frame_interval(struct ov965x *ov965x, if (mbus_fmt->width != iv->size.width || mbus_fmt->height != iv->size.height) continue; - err = abs64((u64)(iv->interval.numerator * 10000) / + err = abs((u64)(iv->interval.numerator * 10000) / iv->interval.denominator - req_int); if (err < min_err) { fiv = iv; @@ -1436,7 +1436,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) int ret; mutex_lock(&ov965x->lock); - __ov965x_set_power(ov965x, 1); + __ov965x_set_power(ov965x, 1); usleep_range(25000, 26000); /* Check sensor revision */ diff --git a/kernel/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/kernel/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 08b234bd2..51b260104 100644 --- a/kernel/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/kernel/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -167,7 +167,7 @@ static int s5c73m3_i2c_read(struct i2c_client *client, u16 addr, u16 *data) */ ret = i2c_transfer(client->adapter, msg, 2); if (ret == 2) { - *data = be16_to_cpup((u16 *)rbuf); + *data = be16_to_cpup((__be16 *)rbuf); v4l2_dbg(4, s5c73m3_dbg, client, "%s: addr: 0x%04x, data: 0x%04x\n", __func__, addr, *data); @@ -1453,7 +1453,7 @@ static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) state->apply_fiv = 1; state->apply_fmt = 1; } - } else if (!on == state->power) { + } else if (state->power == !on) { ret = s5c73m3_set_af_softlanding(state); if (!ret) ret = __s5c73m3_power_off(state); diff --git a/kernel/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/kernel/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 63eb19093..72ef9f936 100644 --- a/kernel/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/kernel/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -31,6 +31,7 @@ static const struct of_device_id s5c73m3_spi_ids[] = { { .compatible = "samsung,s5c73m3" }, { } }; +MODULE_DEVICE_TABLE(of, s5c73m3_spi_ids); enum spi_direction { SPI_DIR_RX, @@ -149,8 +150,6 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) spidrv->remove = s5c73m3_spi_remove; spidrv->probe = s5c73m3_spi_probe; spidrv->driver.name = S5C73M3_SPI_DRV_NAME; - spidrv->driver.bus = &spi_bus_type; - spidrv->driver.owner = THIS_MODULE; spidrv->driver.of_match_table = s5c73m3_spi_ids; return spi_register_driver(spidrv); diff --git a/kernel/drivers/media/i2c/s5k5baf.c b/kernel/drivers/media/i2c/s5k5baf.c index 297ef04e1..774e0d0c9 100644 --- a/kernel/drivers/media/i2c/s5k5baf.c +++ b/kernel/drivers/media/i2c/s5k5baf.c @@ -491,7 +491,7 @@ static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, min(2 * count, 64), seq); - buf[0] = __constant_cpu_to_be16(REG_CMD_BUF); + buf[0] = cpu_to_be16(REG_CMD_BUF); while (count > 0) { int n = min_t(int, count, ARRAY_SIZE(buf) - 1); @@ -1054,7 +1054,7 @@ static int s5k5baf_set_power(struct v4l2_subdev *sd, int on) mutex_lock(&state->lock); - if (!on != state->power) + if (state->power != !on) goto out; if (on) { diff --git a/kernel/drivers/media/i2c/s5k6a3.c b/kernel/drivers/media/i2c/s5k6a3.c index bc389d5e4..b1b1574df 100644 --- a/kernel/drivers/media/i2c/s5k6a3.c +++ b/kernel/drivers/media/i2c/s5k6a3.c @@ -363,6 +363,7 @@ static int s5k6a3_remove(struct i2c_client *client) static const struct i2c_device_id s5k6a3_ids[] = { { } }; +MODULE_DEVICE_TABLE(i2c, s5k6a3_ids); #ifdef CONFIG_OF static const struct of_device_id s5k6a3_of_match[] = { diff --git a/kernel/drivers/media/i2c/s5k6aa.c b/kernel/drivers/media/i2c/s5k6aa.c index de803a11e..d0ad6a25b 100644 --- a/kernel/drivers/media/i2c/s5k6aa.c +++ b/kernel/drivers/media/i2c/s5k6aa.c @@ -875,7 +875,7 @@ static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) mutex_lock(&s5k6aa->lock); - if (!on == s5k6aa->power) { + if (s5k6aa->power == !on) { if (on) { ret = __s5k6aa_power_on(s5k6aa); if (!ret) diff --git a/kernel/drivers/media/i2c/saa6588.c b/kernel/drivers/media/i2c/saa6588.c index 2960b5a83..37e65f661 100644 --- a/kernel/drivers/media/i2c/saa6588.c +++ b/kernel/drivers/media/i2c/saa6588.c @@ -301,9 +301,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) first and the last of the 3 bytes block. */ - tmp = tmpbuf[2]; - tmpbuf[2] = tmpbuf[0]; - tmpbuf[0] = tmp; + swap(tmpbuf[2], tmpbuf[0]); /* Map 'Invalid block E' to 'Invalid Block' */ if (blocknum == 6) @@ -520,7 +518,6 @@ MODULE_DEVICE_TABLE(i2c, saa6588_id); static struct i2c_driver saa6588_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa6588", }, .probe = saa6588_probe, diff --git a/kernel/drivers/media/i2c/saa6752hs.c b/kernel/drivers/media/i2c/saa6752hs.c index f14c0e643..7202d3a32 100644 --- a/kernel/drivers/media/i2c/saa6752hs.c +++ b/kernel/drivers/media/i2c/saa6752hs.c @@ -554,10 +554,16 @@ static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) return 0; } -static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +static int saa6752hs_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *f = &format->format; struct saa6752hs_state *h = to_state(sd); + if (format->pad) + return -EINVAL; + if (h->video_format == SAA6752HS_VF_UNKNOWN) h->video_format = SAA6752HS_VF_D1; f->width = v4l2_format_table[h->video_format].fmt.pix.width; @@ -568,10 +574,17 @@ static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefm return 0; } -static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +static int saa6752hs_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *f = &format->format; + struct saa6752hs_state *h = to_state(sd); int dist_352, dist_480, dist_720; + if (format->pad) + return -EINVAL; + f->code = MEDIA_BUS_FMT_FIXED; dist_352 = abs(f->width - 352); @@ -592,15 +605,11 @@ static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_frame } f->field = V4L2_FIELD_INTERLACED; f->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct saa6752hs_state *h = to_state(sd); - if (f->code != MEDIA_BUS_FMT_FIXED) - return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *f; + return 0; + } /* FIXME: translate and round width/height into EMPRESS @@ -614,7 +623,9 @@ static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefm D1 | 720x576 | 720x480 */ - saa6752hs_try_mbus_fmt(sd, f); + if (f->code != MEDIA_BUS_FMT_FIXED) + return -EINVAL; + if (f->width == 720) h->video_format = SAA6752HS_VF_D1; else if (f->width == 480) @@ -647,14 +658,17 @@ static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { .s_std = saa6752hs_s_std, - .s_mbus_fmt = saa6752hs_s_mbus_fmt, - .try_mbus_fmt = saa6752hs_try_mbus_fmt, - .g_mbus_fmt = saa6752hs_g_mbus_fmt, +}; + +static const struct v4l2_subdev_pad_ops saa6752hs_pad_ops = { + .get_fmt = saa6752hs_get_fmt, + .set_fmt = saa6752hs_set_fmt, }; static const struct v4l2_subdev_ops saa6752hs_ops = { .core = &saa6752hs_core_ops, .video = &saa6752hs_video_ops, + .pad = &saa6752hs_pad_ops, }; static int saa6752hs_probe(struct i2c_client *client, @@ -779,7 +793,6 @@ MODULE_DEVICE_TABLE(i2c, saa6752hs_id); static struct i2c_driver saa6752hs_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa6752hs", }, .probe = saa6752hs_probe, diff --git a/kernel/drivers/media/i2c/saa7110.c b/kernel/drivers/media/i2c/saa7110.c index 99689ee57..6f4988680 100644 --- a/kernel/drivers/media/i2c/saa7110.c +++ b/kernel/drivers/media/i2c/saa7110.c @@ -357,16 +357,6 @@ static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { .s_ctrl = saa7110_s_ctrl, }; -static const struct v4l2_subdev_core_ops saa7110_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - static const struct v4l2_subdev_video_ops saa7110_video_ops = { .s_std = saa7110_s_std, .s_routing = saa7110_s_routing, @@ -376,7 +366,6 @@ static const struct v4l2_subdev_video_ops saa7110_video_ops = { }; static const struct v4l2_subdev_ops saa7110_ops = { - .core = &saa7110_core_ops, .video = &saa7110_video_ops, }; @@ -472,7 +461,6 @@ MODULE_DEVICE_TABLE(i2c, saa7110_id); static struct i2c_driver saa7110_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7110", }, .probe = saa7110_probe, diff --git a/kernel/drivers/media/i2c/saa7115.c b/kernel/drivers/media/i2c/saa7115.c index 7147c8b68..91e75222c 100644 --- a/kernel/drivers/media/i2c/saa7115.c +++ b/kernel/drivers/media/i2c/saa7115.c @@ -1170,12 +1170,18 @@ static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f return 0; } -static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +static int saa711x_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { - if (fmt->code != MEDIA_BUS_FMT_FIXED) + struct v4l2_mbus_framefmt *fmt = &format->format; + + if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) return -EINVAL; fmt->field = V4L2_FIELD_INTERLACED; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; return saa711x_set_size(sd, fmt->width, fmt->height); } @@ -1603,7 +1609,6 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = { .s_std = saa711x_s_std, .s_routing = saa711x_s_routing, .s_crystal_freq = saa711x_s_crystal_freq, - .s_mbus_fmt = saa711x_s_mbus_fmt, .s_stream = saa711x_s_stream, .querystd = saa711x_querystd, .g_input_status = saa711x_g_input_status, @@ -1617,12 +1622,17 @@ static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { .s_raw_fmt = saa711x_s_raw_fmt, }; +static const struct v4l2_subdev_pad_ops saa711x_pad_ops = { + .set_fmt = saa711x_set_fmt, +}; + static const struct v4l2_subdev_ops saa711x_ops = { .core = &saa711x_core_ops, .tuner = &saa711x_tuner_ops, .audio = &saa711x_audio_ops, .video = &saa711x_video_ops, .vbi = &saa711x_vbi_ops, + .pad = &saa711x_pad_ops, }; #define CHIP_VER_SIZE 16 @@ -1919,7 +1929,6 @@ MODULE_DEVICE_TABLE(i2c, saa711x_id); static struct i2c_driver saa711x_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7115", }, .probe = saa711x_probe, diff --git a/kernel/drivers/media/i2c/saa7127.c b/kernel/drivers/media/i2c/saa7127.c index 264b755be..a43d96da1 100644 --- a/kernel/drivers/media/i2c/saa7127.c +++ b/kernel/drivers/media/i2c/saa7127.c @@ -822,7 +822,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct i2c_driver saa7127_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7127", }, .probe = saa7127_probe, diff --git a/kernel/drivers/media/i2c/saa717x.c b/kernel/drivers/media/i2c/saa717x.c index 0d0f9a917..1baca37f3 100644 --- a/kernel/drivers/media/i2c/saa717x.c +++ b/kernel/drivers/media/i2c/saa717x.c @@ -152,9 +152,9 @@ static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) i2c_transfer(adap, msgs, 2); if (fw_addr) - value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); + value = (mm2[2] << 16) | (mm2[1] << 8) | mm2[0]; else - value = mm2[0] & 0xff; + value = mm2[0]; v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); return value; @@ -992,13 +992,16 @@ static int saa717x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regi } #endif -static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +static int saa717x_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; int prescale, h_scale, v_scale; v4l2_dbg(1, debug, sd, "decoder set size\n"); - if (fmt->code != MEDIA_BUS_FMT_FIXED) + if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) return -EINVAL; /* FIXME need better bounds checking here */ @@ -1010,6 +1013,9 @@ static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt fmt->field = V4L2_FIELD_INTERLACED; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + /* scaling setting */ /* NTSC and interlace only */ prescale = SAA717X_NTSC_WIDTH / fmt->width; @@ -1198,13 +1204,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = { .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .log_status = saa717x_log_status, }; @@ -1217,7 +1216,6 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { static const struct v4l2_subdev_video_ops saa717x_video_ops = { .s_std = saa717x_s_std, .s_routing = saa717x_s_video_routing, - .s_mbus_fmt = saa717x_s_mbus_fmt, .s_stream = saa717x_s_stream, }; @@ -1225,11 +1223,16 @@ static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { .s_routing = saa717x_s_audio_routing, }; +static const struct v4l2_subdev_pad_ops saa717x_pad_ops = { + .set_fmt = saa717x_set_fmt, +}; + static const struct v4l2_subdev_ops saa717x_ops = { .core = &saa717x_core_ops, .tuner = &saa717x_tuner_ops, .audio = &saa717x_audio_ops, .video = &saa717x_video_ops, + .pad = &saa717x_pad_ops, }; /* ----------------------------------------------------------------------- */ @@ -1353,7 +1356,6 @@ MODULE_DEVICE_TABLE(i2c, saa717x_id); static struct i2c_driver saa717x_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa717x", }, .probe = saa717x_probe, diff --git a/kernel/drivers/media/i2c/saa7185.c b/kernel/drivers/media/i2c/saa7185.c index f56c1c88b..eecad2d1e 100644 --- a/kernel/drivers/media/i2c/saa7185.c +++ b/kernel/drivers/media/i2c/saa7185.c @@ -356,7 +356,6 @@ MODULE_DEVICE_TABLE(i2c, saa7185_id); static struct i2c_driver saa7185_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7185", }, .probe = saa7185_probe, diff --git a/kernel/drivers/media/i2c/smiapp/smiapp-core.c b/kernel/drivers/media/i2c/smiapp/smiapp-core.c index 557f25def..fb39dfd55 100644 --- a/kernel/drivers/media/i2c/smiapp/smiapp-core.c +++ b/kernel/drivers/media/i2c/smiapp/smiapp-core.c @@ -2975,9 +2975,9 @@ static int smiapp_resume(struct device *dev) static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) { struct smiapp_platform_data *pdata; - struct v4l2_of_endpoint bus_cfg; + struct v4l2_of_endpoint *bus_cfg; struct device_node *ep; - uint32_t asize; + int i; int rval; if (!dev->of_node) @@ -2987,13 +2987,15 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) if (!ep) return NULL; + bus_cfg = v4l2_of_alloc_parse_endpoint(ep); + if (IS_ERR(bus_cfg)) + goto out_err; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) goto out_err; - v4l2_of_parse_endpoint(ep, &bus_cfg); - - switch (bus_cfg.bus_type) { + switch (bus_cfg->bus_type) { case V4L2_MBUS_CSI2: pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; break; @@ -3002,7 +3004,7 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) goto out_err; } - pdata->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + pdata->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes; dev_dbg(dev, "lanes %u\n", pdata->lanes); /* xshutdown GPIO is optional */ @@ -3022,34 +3024,30 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown, pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode); - rval = of_get_property(ep, "link-frequencies", &asize) ? 0 : -ENOENT; - if (rval) { - dev_warn(dev, "can't get link-frequencies array size\n"); + if (!bus_cfg->nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined\n"); goto out_err; } - pdata->op_sys_clock = devm_kzalloc(dev, asize, GFP_KERNEL); + pdata->op_sys_clock = devm_kcalloc( + dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */, + sizeof(*pdata->op_sys_clock), GFP_KERNEL); if (!pdata->op_sys_clock) { rval = -ENOMEM; goto out_err; } - asize /= sizeof(*pdata->op_sys_clock); - rval = of_property_read_u64_array( - ep, "link-frequencies", pdata->op_sys_clock, asize); - if (rval) { - dev_warn(dev, "can't get link-frequencies\n"); - goto out_err; + for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) { + pdata->op_sys_clock[i] = bus_cfg->link_frequencies[i]; + dev_dbg(dev, "freq %d: %lld\n", i, pdata->op_sys_clock[i]); } - for (; asize > 0; asize--) - dev_dbg(dev, "freq %d: %lld\n", asize - 1, - pdata->op_sys_clock[asize - 1]); - + v4l2_of_free_endpoint(bus_cfg); of_node_put(ep); return pdata; out_err: + v4l2_of_free_endpoint(bus_cfg); of_node_put(ep); return NULL; } @@ -3133,6 +3131,7 @@ static const struct of_device_id smiapp_of_table[] = { { .compatible = "nokia,smia" }, { }, }; +MODULE_DEVICE_TABLE(of, smiapp_of_table); static const struct i2c_device_id smiapp_id_table[] = { { SMIAPP_NAME, 0 }, diff --git a/kernel/drivers/media/i2c/soc_camera/imx074.c b/kernel/drivers/media/i2c/soc_camera/imx074.c index ec89cfa92..f68c2352c 100644 --- a/kernel/drivers/media/i2c/soc_camera/imx074.c +++ b/kernel/drivers/media/i2c/soc_camera/imx074.c @@ -153,14 +153,24 @@ static int reg_read(struct i2c_client *client, const u16 addr) return buf[0] & 0xff; /* no sign-extension */ } -static int imx074_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int imx074_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx074 *priv = to_imx074(client); + + if (format->pad) + return -EINVAL; dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); if (!fmt) { + /* MIPI CSI could have changed the format, double-check */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; mf->code = imx074_colour_fmts[0].code; mf->colorspace = imx074_colour_fmts[0].colorspace; } @@ -169,36 +179,27 @@ static int imx074_try_fmt(struct v4l2_subdev *sd, mf->height = IMX074_HEIGHT; mf->field = V4L2_FIELD_NONE; - return 0; -} - -static int imx074_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - /* MIPI CSI could have changed the format, double-check */ - if (!imx074_find_datafmt(mf->code)) - return -EINVAL; - - imx074_try_fmt(sd, mf); - - priv->fmt = imx074_find_datafmt(mf->code); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->fmt = imx074_find_datafmt(mf->code); + else + cfg->try_fmt = *mf; return 0; } -static int imx074_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int imx074_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct imx074 *priv = to_imx074(client); const struct imx074_datafmt *fmt = priv->fmt; + if (format->pad) + return -EINVAL; + mf->code = fmt->code; mf->colorspace = fmt->colorspace; mf->width = IMX074_WIDTH; @@ -235,13 +236,15 @@ static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int imx074_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts)) + if (code->pad || + (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) return -EINVAL; - *code = imx074_colour_fmts[index].code; + code->code = imx074_colour_fmts[code->index].code; return 0; } @@ -275,10 +278,6 @@ static int imx074_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .s_stream = imx074_s_stream, - .s_mbus_fmt = imx074_s_fmt, - .g_mbus_fmt = imx074_g_fmt, - .try_mbus_fmt = imx074_try_fmt, - .enum_mbus_fmt = imx074_enum_fmt, .g_crop = imx074_g_crop, .cropcap = imx074_cropcap, .g_mbus_config = imx074_g_mbus_config, @@ -288,9 +287,16 @@ static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { .s_power = imx074_s_power, }; +static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { + .enum_mbus_code = imx074_enum_mbus_code, + .get_fmt = imx074_get_fmt, + .set_fmt = imx074_set_fmt, +}; + static struct v4l2_subdev_ops imx074_subdev_ops = { .core = &imx074_subdev_core_ops, .video = &imx074_subdev_video_ops, + .pad = &imx074_subdev_pad_ops, }; static int imx074_video_probe(struct i2c_client *client) diff --git a/kernel/drivers/media/i2c/soc_camera/mt9m001.c b/kernel/drivers/media/i2c/soc_camera/mt9m001.c index 2e9a53502..4fbdd1e9f 100644 --- a/kernel/drivers/media/i2c/soc_camera/mt9m001.c +++ b/kernel/drivers/media/i2c/soc_camera/mt9m001.c @@ -205,7 +205,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) /* * The caller provides a supported format, as verified per - * call to .try_mbus_fmt() + * call to .set_fmt(FORMAT_TRY). */ if (!ret) ret = reg_write(client, MT9M001_COLUMN_START, rect.left); @@ -250,11 +250,16 @@ static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int mt9m001_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9m001_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; mf->width = mt9m001->rect.width; mf->height = mt9m001->rect.height; @@ -293,13 +298,18 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd, return ret; } -static int mt9m001_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9m001_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); const struct mt9m001_datafmt *fmt; + if (format->pad) + return -EINVAL; + v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH, 1, &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, @@ -317,6 +327,9 @@ static int mt9m001_try_fmt(struct v4l2_subdev *sd, mf->colorspace = fmt->colorspace; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9m001_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } @@ -562,16 +575,17 @@ static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { .s_power = mt9m001_s_power, }; -static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); - if (index >= mt9m001->num_fmts) + if (code->pad || code->index >= mt9m001->num_fmts) return -EINVAL; - *code = mt9m001->fmts[index].code; + code->code = mt9m001->fmts[code->index].code; return 0; } @@ -611,13 +625,9 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, - .s_mbus_fmt = mt9m001_s_fmt, - .g_mbus_fmt = mt9m001_g_fmt, - .try_mbus_fmt = mt9m001_try_fmt, .s_crop = mt9m001_s_crop, .g_crop = mt9m001_g_crop, .cropcap = mt9m001_cropcap, - .enum_mbus_fmt = mt9m001_enum_fmt, .g_mbus_config = mt9m001_g_mbus_config, .s_mbus_config = mt9m001_s_mbus_config, }; @@ -626,10 +636,17 @@ static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { .g_skip_top_lines = mt9m001_g_skip_top_lines, }; +static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { + .enum_mbus_code = mt9m001_enum_mbus_code, + .get_fmt = mt9m001_get_fmt, + .set_fmt = mt9m001_set_fmt, +}; + static struct v4l2_subdev_ops mt9m001_subdev_ops = { .core = &mt9m001_subdev_core_ops, .video = &mt9m001_subdev_video_ops, .sensor = &mt9m001_subdev_sensor_ops, + .pad = &mt9m001_subdev_pad_ops, }; static int mt9m001_probe(struct i2c_client *client, diff --git a/kernel/drivers/media/i2c/soc_camera/mt9m111.c b/kernel/drivers/media/i2c/soc_camera/mt9m111.c index 441e0fda2..6dfaead6a 100644 --- a/kernel/drivers/media/i2c/soc_camera/mt9m111.c +++ b/kernel/drivers/media/i2c/soc_camera/mt9m111.c @@ -447,11 +447,16 @@ static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int mt9m111_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9m111_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + if (format->pad) + return -EINVAL; + mf->width = mt9m111->width; mf->height = mt9m111->height; mf->code = mt9m111->fmt->code; @@ -531,14 +536,20 @@ static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111, return ret; } -static int mt9m111_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9m111_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); const struct mt9m111_datafmt *fmt; struct v4l2_rect *rect = &mt9m111->rect; bool bayer; + int ret; + + if (format->pad) + return -EINVAL; fmt = mt9m111_find_datafmt(mt9m111, mf->code); @@ -572,20 +583,10 @@ static int mt9m111_try_fmt(struct v4l2_subdev *sd, mf->code = fmt->code; mf->colorspace = fmt->colorspace; - return 0; -} - -static int mt9m111_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - const struct mt9m111_datafmt *fmt; - struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); - struct v4l2_rect *rect = &mt9m111->rect; - int ret; - - mt9m111_try_fmt(sd, mf); - fmt = mt9m111_find_datafmt(mt9m111, mf->code); - /* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */ + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code); if (!ret) @@ -839,13 +840,14 @@ static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { #endif }; -static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(mt9m111_colour_fmts)) + if (code->pad || code->index >= ARRAY_SIZE(mt9m111_colour_fmts)) return -EINVAL; - *code = mt9m111_colour_fmts[index].code; + code->code = mt9m111_colour_fmts[code->index].code; return 0; } @@ -865,19 +867,22 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, } static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { - .s_mbus_fmt = mt9m111_s_fmt, - .g_mbus_fmt = mt9m111_g_fmt, - .try_mbus_fmt = mt9m111_try_fmt, .s_crop = mt9m111_s_crop, .g_crop = mt9m111_g_crop, .cropcap = mt9m111_cropcap, - .enum_mbus_fmt = mt9m111_enum_fmt, .g_mbus_config = mt9m111_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { + .enum_mbus_code = mt9m111_enum_mbus_code, + .get_fmt = mt9m111_get_fmt, + .set_fmt = mt9m111_set_fmt, +}; + static struct v4l2_subdev_ops mt9m111_subdev_ops = { .core = &mt9m111_subdev_core_ops, .video = &mt9m111_subdev_video_ops, + .pad = &mt9m111_subdev_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/soc_camera/mt9t031.c b/kernel/drivers/media/i2c/soc_camera/mt9t031.c index 35d9c8d25..3b6eeed2e 100644 --- a/kernel/drivers/media/i2c/soc_camera/mt9t031.c +++ b/kernel/drivers/media/i2c/soc_camera/mt9t031.c @@ -264,7 +264,7 @@ static int mt9t031_set_params(struct i2c_client *client, /* * The caller provides a supported format, as guaranteed by - * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap() + * .set_fmt(FORMAT_TRY), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) ret = reg_write(client, MT9T031_COLUMN_START, rect->left); @@ -337,12 +337,17 @@ static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int mt9t031_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9t031_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); + if (format->pad) + return -EINVAL; + mf->width = mt9t031->rect.width / mt9t031->xskip; mf->height = mt9t031->rect.height / mt9t031->yskip; mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; @@ -352,16 +357,36 @@ static int mt9t031_g_fmt(struct v4l2_subdev *sd, return 0; } -static int mt9t031_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +/* + * If a user window larger than sensor window is requested, we'll increase the + * sensor window. + */ +static int mt9t031_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); u16 xskip, yskip; struct v4l2_rect rect = mt9t031->rect; + if (format->pad) + return -EINVAL; + + mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; + mf->colorspace = V4L2_COLORSPACE_SRGB; + v4l_bound_align_image( + &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, + &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + /* - * try_fmt has put width and height within limits. + * Width and height are within limits. * S_FMT: use binning and skipping for scaling */ xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); @@ -374,23 +399,6 @@ static int mt9t031_s_fmt(struct v4l2_subdev *sd, return mt9t031_set_params(client, &rect, xskip, yskip); } -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9t031_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) @@ -672,13 +680,14 @@ static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { #endif }; -static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + if (code->pad || code->index) return -EINVAL; - *code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; return 0; } @@ -712,13 +721,9 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .s_stream = mt9t031_s_stream, - .s_mbus_fmt = mt9t031_s_fmt, - .g_mbus_fmt = mt9t031_g_fmt, - .try_mbus_fmt = mt9t031_try_fmt, .s_crop = mt9t031_s_crop, .g_crop = mt9t031_g_crop, .cropcap = mt9t031_cropcap, - .enum_mbus_fmt = mt9t031_enum_fmt, .g_mbus_config = mt9t031_g_mbus_config, .s_mbus_config = mt9t031_s_mbus_config, }; @@ -727,10 +732,17 @@ static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { .g_skip_top_lines = mt9t031_g_skip_top_lines, }; +static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { + .enum_mbus_code = mt9t031_enum_mbus_code, + .get_fmt = mt9t031_get_fmt, + .set_fmt = mt9t031_set_fmt, +}; + static struct v4l2_subdev_ops mt9t031_subdev_ops = { .core = &mt9t031_subdev_core_ops, .video = &mt9t031_subdev_video_ops, .sensor = &mt9t031_subdev_sensor_ops, + .pad = &mt9t031_subdev_pad_ops, }; static int mt9t031_probe(struct i2c_client *client, diff --git a/kernel/drivers/media/i2c/soc_camera/mt9t112.c b/kernel/drivers/media/i2c/soc_camera/mt9t112.c index 64f08365e..2f35d31ca 100644 --- a/kernel/drivers/media/i2c/soc_camera/mt9t112.c +++ b/kernel/drivers/media/i2c/soc_camera/mt9t112.c @@ -104,22 +104,22 @@ struct mt9t112_priv { static const struct mt9t112_format mt9t112_cfmts[] = { { .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 0, }, { .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 1, }, { .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 2, }, { .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 3, }, { @@ -904,12 +904,17 @@ static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return mt9t112_set_params(priv, rect, priv->format->code); } -static int mt9t112_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9t112_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); + if (format->pad) + return -EINVAL; + mf->width = priv->frame.width; mf->height = priv->frame.height; mf->colorspace = priv->format->colorspace; @@ -940,14 +945,19 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd, return ret; } -static int mt9t112_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9t112_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); unsigned int top, left; int i; + if (format->pad) + return -EINVAL; + for (i = 0; i < priv->num_formats; i++) if (mt9t112_cfmts[i].code == mf->code) break; @@ -963,19 +973,23 @@ static int mt9t112_try_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9t112_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } -static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - if (index >= priv->num_formats) + if (code->pad || code->index >= priv->num_formats) return -EINVAL; - *code = mt9t112_cfmts[index].code; + code->code = mt9t112_cfmts[code->index].code; return 0; } @@ -1010,23 +1024,26 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { .s_stream = mt9t112_s_stream, - .g_mbus_fmt = mt9t112_g_fmt, - .s_mbus_fmt = mt9t112_s_fmt, - .try_mbus_fmt = mt9t112_try_fmt, .cropcap = mt9t112_cropcap, .g_crop = mt9t112_g_crop, .s_crop = mt9t112_s_crop, - .enum_mbus_fmt = mt9t112_enum_fmt, .g_mbus_config = mt9t112_g_mbus_config, .s_mbus_config = mt9t112_s_mbus_config, }; +static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { + .enum_mbus_code = mt9t112_enum_mbus_code, + .get_fmt = mt9t112_get_fmt, + .set_fmt = mt9t112_set_fmt, +}; + /************************************************************************ i2c driver ************************************************************************/ static struct v4l2_subdev_ops mt9t112_subdev_ops = { .core = &mt9t112_subdev_core_ops, .video = &mt9t112_subdev_video_ops, + .pad = &mt9t112_subdev_pad_ops, }; static int mt9t112_camera_probe(struct i2c_client *client) diff --git a/kernel/drivers/media/i2c/soc_camera/mt9v022.c b/kernel/drivers/media/i2c/soc_camera/mt9v022.c index a246d4d64..f31377408 100644 --- a/kernel/drivers/media/i2c/soc_camera/mt9v022.c +++ b/kernel/drivers/media/i2c/soc_camera/mt9v022.c @@ -375,12 +375,17 @@ static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int mt9v022_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9v022_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); + if (format->pad) + return -EINVAL; + mf->width = mt9v022->rect.width; mf->height = mt9v022->rect.height; mf->code = mt9v022->fmt->code; @@ -407,7 +412,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, /* * The caller provides a supported format, as verified per call to - * .try_mbus_fmt(), datawidth is from our supported format list + * .set_fmt(FORMAT_TRY), datawidth is from our supported format list */ switch (mf->code) { case MEDIA_BUS_FMT_Y8_1X8: @@ -437,15 +442,20 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, return ret; } -static int mt9v022_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int mt9v022_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); const struct mt9v022_datafmt *fmt; int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; + if (format->pad) + return -EINVAL; + v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH, align, &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, @@ -460,6 +470,9 @@ static int mt9v022_try_fmt(struct v4l2_subdev *sd, mf->colorspace = fmt->colorspace; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9v022_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } @@ -758,16 +771,17 @@ static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { .s_power = mt9v022_s_power, }; -static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); - if (index >= mt9v022->num_fmts) + if (code->pad || code->index >= mt9v022->num_fmts) return -EINVAL; - *code = mt9v022->fmts[index].code; + code->code = mt9v022->fmts[code->index].code; return 0; } @@ -839,13 +853,9 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { .s_stream = mt9v022_s_stream, - .s_mbus_fmt = mt9v022_s_fmt, - .g_mbus_fmt = mt9v022_g_fmt, - .try_mbus_fmt = mt9v022_try_fmt, .s_crop = mt9v022_s_crop, .g_crop = mt9v022_g_crop, .cropcap = mt9v022_cropcap, - .enum_mbus_fmt = mt9v022_enum_fmt, .g_mbus_config = mt9v022_g_mbus_config, .s_mbus_config = mt9v022_s_mbus_config, }; @@ -854,10 +864,17 @@ static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { .g_skip_top_lines = mt9v022_g_skip_top_lines, }; +static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { + .enum_mbus_code = mt9v022_enum_mbus_code, + .get_fmt = mt9v022_get_fmt, + .set_fmt = mt9v022_set_fmt, +}; + static struct v4l2_subdev_ops mt9v022_subdev_ops = { .core = &mt9v022_subdev_core_ops, .video = &mt9v022_subdev_video_ops, .sensor = &mt9v022_subdev_sensor_ops, + .pad = &mt9v022_subdev_pad_ops, }; static int mt9v022_probe(struct i2c_client *client, diff --git a/kernel/drivers/media/i2c/soc_camera/ov2640.c b/kernel/drivers/media/i2c/soc_camera/ov2640.c index e3c907a97..9b4f5deec 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov2640.c +++ b/kernel/drivers/media/i2c/soc_camera/ov2640.c @@ -845,12 +845,17 @@ err: return ret; } -static int ov2640_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov2640_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov2640_priv *priv = to_ov2640(client); + if (format->pad) + return -EINVAL; + if (!priv->win) { u32 width = SVGA_WIDTH, height = SVGA_HEIGHT; priv->win = ov2640_select_win(&width, &height); @@ -876,33 +881,16 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd, return 0; } -static int ov2640_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov2640_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; + if (format->pad) + return -EINVAL; - switch (mf->code) { - case MEDIA_BUS_FMT_RGB565_2X8_BE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - mf->colorspace = V4L2_COLORSPACE_SRGB; - break; - default: - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; - } - - ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code); - - return ret; -} - -static int ov2640_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ /* * select suitable win, but don't store it */ @@ -922,16 +910,21 @@ static int ov2640_try_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; } + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov2640_set_params(client, &mf->width, + &mf->height, mf->code); + cfg->try_fmt = *mf; return 0; } -static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov2640_codes)) + if (code->pad || code->index >= ARRAY_SIZE(ov2640_codes)) return -EINVAL; - *code = ov2640_codes[index]; + code->code = ov2640_codes[code->index]; return 0; } @@ -1031,18 +1024,21 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { .s_stream = ov2640_s_stream, - .g_mbus_fmt = ov2640_g_fmt, - .s_mbus_fmt = ov2640_s_fmt, - .try_mbus_fmt = ov2640_try_fmt, .cropcap = ov2640_cropcap, .g_crop = ov2640_g_crop, - .enum_mbus_fmt = ov2640_enum_fmt, .g_mbus_config = ov2640_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { + .enum_mbus_code = ov2640_enum_mbus_code, + .get_fmt = ov2640_get_fmt, + .set_fmt = ov2640_set_fmt, +}; + static struct v4l2_subdev_ops ov2640_subdev_ops = { .core = &ov2640_subdev_core_ops, .video = &ov2640_subdev_video_ops, + .pad = &ov2640_subdev_pad_ops, }; /* OF probe functions */ diff --git a/kernel/drivers/media/i2c/soc_camera/ov5642.c b/kernel/drivers/media/i2c/soc_camera/ov5642.c index 93ae031bd..bab9ac0c1 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov5642.c +++ b/kernel/drivers/media/i2c/soc_camera/ov5642.c @@ -786,50 +786,50 @@ static int ov5642_set_resolution(struct v4l2_subdev *sd) return ret; } -static int ov5642_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov5642_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); + if (format->pad) + return -EINVAL; + mf->width = priv->crop_rect.width; mf->height = priv->crop_rect.height; if (!fmt) { + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; mf->code = ov5642_colour_fmts[0].code; mf->colorspace = ov5642_colour_fmts[0].colorspace; } mf->field = V4L2_FIELD_NONE; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->fmt = ov5642_find_datafmt(mf->code); + else + cfg->try_fmt = *mf; return 0; } -static int ov5642_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - /* MIPI CSI could have changed the format, double-check */ - if (!ov5642_find_datafmt(mf->code)) - return -EINVAL; - - ov5642_try_fmt(sd, mf); - priv->fmt = ov5642_find_datafmt(mf->code); - - return 0; -} - -static int ov5642_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov5642_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); const struct ov5642_datafmt *fmt = priv->fmt; + if (format->pad) + return -EINVAL; + mf->code = fmt->code; mf->colorspace = fmt->colorspace; mf->width = priv->crop_rect.width; @@ -839,13 +839,14 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd, return 0; } -static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov5642_colour_fmts)) + if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) return -EINVAL; - *code = ov5642_colour_fmts[index].code; + code->code = ov5642_colour_fmts[code->index].code; return 0; } @@ -939,16 +940,18 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on) } static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .s_mbus_fmt = ov5642_s_fmt, - .g_mbus_fmt = ov5642_g_fmt, - .try_mbus_fmt = ov5642_try_fmt, - .enum_mbus_fmt = ov5642_enum_fmt, .s_crop = ov5642_s_crop, .g_crop = ov5642_g_crop, .cropcap = ov5642_cropcap, .g_mbus_config = ov5642_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { + .enum_mbus_code = ov5642_enum_mbus_code, + .get_fmt = ov5642_get_fmt, + .set_fmt = ov5642_set_fmt, +}; + static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { .s_power = ov5642_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -960,6 +963,7 @@ static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { static struct v4l2_subdev_ops ov5642_subdev_ops = { .core = &ov5642_subdev_core_ops, .video = &ov5642_subdev_video_ops, + .pad = &ov5642_subdev_pad_ops, }; static int ov5642_video_probe(struct i2c_client *client) diff --git a/kernel/drivers/media/i2c/soc_camera/ov6650.c b/kernel/drivers/media/i2c/soc_camera/ov6650.c index f4eef2fa6..1f8af1ee8 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov6650.c +++ b/kernel/drivers/media/i2c/soc_camera/ov6650.c @@ -499,12 +499,17 @@ static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int ov6650_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov6650_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); + if (format->pad) + return -EINVAL; + mf->width = priv->rect.width >> priv->half_scale; mf->height = priv->rect.height >> priv->half_scale; mf->code = priv->code; @@ -680,16 +685,20 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) mf->width = priv->rect.width >> half_scale; mf->height = priv->rect.height >> half_scale; } - return ret; } -static int ov6650_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov6650_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); + if (format->pad) + return -EINVAL; + if (is_unscaled_ok(mf->width, mf->height, &priv->rect)) v4l_bound_align_image(&mf->width, 2, W_CIF, 1, &mf->height, 2, H_CIF, 1, 0); @@ -713,16 +722,21 @@ static int ov6650_try_fmt(struct v4l2_subdev *sd, break; } + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov6650_s_fmt(sd, mf); + cfg->try_fmt = *mf; + return 0; } -static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov6650_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov6650_codes)) + if (code->pad || code->index >= ARRAY_SIZE(ov6650_codes)) return -EINVAL; - *code = ov6650_codes[index]; + code->code = ov6650_codes[code->index]; return 0; } @@ -929,10 +943,6 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, - .g_mbus_fmt = ov6650_g_fmt, - .s_mbus_fmt = ov6650_s_fmt, - .try_mbus_fmt = ov6650_try_fmt, - .enum_mbus_fmt = ov6650_enum_fmt, .cropcap = ov6650_cropcap, .g_crop = ov6650_g_crop, .s_crop = ov6650_s_crop, @@ -942,9 +952,16 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = { .s_mbus_config = ov6650_s_mbus_config, }; +static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { + .enum_mbus_code = ov6650_enum_mbus_code, + .get_fmt = ov6650_get_fmt, + .set_fmt = ov6650_set_fmt, +}; + static struct v4l2_subdev_ops ov6650_subdev_ops = { .core = &ov6650_core_ops, .video = &ov6650_video_ops, + .pad = &ov6650_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/soc_camera/ov772x.c b/kernel/drivers/media/i2c/soc_camera/ov772x.c index 8daac88b3..f150a8bd9 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov772x.c +++ b/kernel/drivers/media/i2c/soc_camera/ov772x.c @@ -876,11 +876,16 @@ static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int ov772x_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov772x_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct ov772x_priv *priv = to_ov772x(sd); + if (format->pad) + return -EINVAL; + mf->width = priv->win->rect.width; mf->height = priv->win->rect.height; mf->code = priv->cfmt->code; @@ -915,12 +920,17 @@ static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) return 0; } -static int ov772x_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov772x_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; + if (format->pad) + return -EINVAL; + ov772x_select_params(mf, &cfmt, &win); mf->code = cfmt->code; @@ -929,6 +939,9 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; mf->colorspace = cfmt->colorspace; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov772x_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } @@ -989,13 +1002,14 @@ static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { .s_power = ov772x_s_power, }; -static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov772x_cfmts)) + if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) return -EINVAL; - *code = ov772x_cfmts[index].code; + code->code = ov772x_cfmts[code->index].code; return 0; } @@ -1016,18 +1030,21 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .s_stream = ov772x_s_stream, - .g_mbus_fmt = ov772x_g_fmt, - .s_mbus_fmt = ov772x_s_fmt, - .try_mbus_fmt = ov772x_try_fmt, .cropcap = ov772x_cropcap, .g_crop = ov772x_g_crop, - .enum_mbus_fmt = ov772x_enum_fmt, .g_mbus_config = ov772x_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { + .enum_mbus_code = ov772x_enum_mbus_code, + .get_fmt = ov772x_get_fmt, + .set_fmt = ov772x_set_fmt, +}; + static struct v4l2_subdev_ops ov772x_subdev_ops = { .core = &ov772x_subdev_core_ops, .video = &ov772x_subdev_video_ops, + .pad = &ov772x_subdev_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/soc_camera/ov9640.c b/kernel/drivers/media/i2c/soc_camera/ov9640.c index aa93d2e88..8caae1c07 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov9640.c +++ b/kernel/drivers/media/i2c/soc_camera/ov9640.c @@ -519,9 +519,15 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, return ret; } -static int ov9640_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov9640_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + ov9640_res_roundup(&mf->width, &mf->height); mf->field = V4L2_FIELD_NONE; @@ -537,16 +543,21 @@ static int ov9640_try_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; } + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9640_s_fmt(sd, mf); + + cfg->try_fmt = *mf; return 0; } -static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov9640_codes)) + if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) return -EINVAL; - *code = ov9640_codes[index]; + code->code = ov9640_codes[code->index]; return 0; } @@ -656,17 +667,20 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov9640_video_ops = { .s_stream = ov9640_s_stream, - .s_mbus_fmt = ov9640_s_fmt, - .try_mbus_fmt = ov9640_try_fmt, - .enum_mbus_fmt = ov9640_enum_fmt, .cropcap = ov9640_cropcap, .g_crop = ov9640_g_crop, .g_mbus_config = ov9640_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { + .enum_mbus_code = ov9640_enum_mbus_code, + .set_fmt = ov9640_set_fmt, +}; + static struct v4l2_subdev_ops ov9640_subdev_ops = { .core = &ov9640_core_ops, .video = &ov9640_video_ops, + .pad = &ov9640_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/soc_camera/ov9740.c b/kernel/drivers/media/i2c/soc_camera/ov9740.c index 841dc5545..03a7fc731 100644 --- a/kernel/drivers/media/i2c/soc_camera/ov9740.c +++ b/kernel/drivers/media/i2c/soc_camera/ov9740.c @@ -704,25 +704,35 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd, return ret; } -static int ov9740_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int ov9740_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + ov9740_res_roundup(&mf->width, &mf->height); mf->field = V4L2_FIELD_NONE; mf->code = MEDIA_BUS_FMT_YUYV8_2X8; mf->colorspace = V4L2_COLORSPACE_SRGB; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9740_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } -static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(ov9740_codes)) + if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) return -EINVAL; - *code = ov9740_codes[index]; + code->code = ov9740_codes[code->index]; return 0; } @@ -904,9 +914,6 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov9740_video_ops = { .s_stream = ov9740_s_stream, - .s_mbus_fmt = ov9740_s_fmt, - .try_mbus_fmt = ov9740_try_fmt, - .enum_mbus_fmt = ov9740_enum_fmt, .cropcap = ov9740_cropcap, .g_crop = ov9740_g_crop, .g_mbus_config = ov9740_g_mbus_config, @@ -920,9 +927,15 @@ static struct v4l2_subdev_core_ops ov9740_core_ops = { #endif }; +static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { + .enum_mbus_code = ov9740_enum_mbus_code, + .set_fmt = ov9740_set_fmt, +}; + static struct v4l2_subdev_ops ov9740_subdev_ops = { - .core = &ov9740_core_ops, - .video = &ov9740_video_ops, + .core = &ov9740_core_ops, + .video = &ov9740_video_ops, + .pad = &ov9740_pad_ops, }; static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { diff --git a/kernel/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/kernel/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 1752428c4..c769cf663 100644 --- a/kernel/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/kernel/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -485,13 +485,14 @@ static int reg_write_multiple(struct i2c_client *client, return 0; } -static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(rj54n1_colour_fmts)) + if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) return -EINVAL; - *code = rj54n1_colour_fmts[index].code; + code->code = rj54n1_colour_fmts[code->index].code; return 0; } @@ -597,12 +598,17 @@ static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int rj54n1_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int rj54n1_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); + if (format->pad) + return -EINVAL; + mf->code = rj54n1->fmt->code; mf->colorspace = rj54n1->fmt->colorspace; mf->field = V4L2_FIELD_NONE; @@ -959,17 +965,25 @@ static int rj54n1_reg_init(struct i2c_client *client) return ret; } -static int rj54n1_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int rj54n1_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); const struct rj54n1_datafmt *fmt; + int output_w, output_h, max_w, max_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; + int ret; + + if (format->pad) + return -EINVAL; dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", __func__, mf->code, mf->width, mf->height); @@ -987,24 +1001,10 @@ static int rj54n1_try_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - return 0; -} - -static int rj54n1_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct rj54n1_datafmt *fmt; - int output_w, output_h, max_w, max_h, - input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; - int ret; - - /* - * The host driver can call us without .try_fmt(), so, we have to take - * care ourseleves - */ - rj54n1_try_fmt(sd, mf); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } /* * Verify if the sensor has just been powered on. TODO: replace this @@ -1020,9 +1020,6 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, return ret; } - dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", - __func__, mf->code, mf->width, mf->height); - /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ switch (mf->code) { case MEDIA_BUS_FMT_YUYV8_2X8: @@ -1249,10 +1246,6 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_stream = rj54n1_s_stream, - .s_mbus_fmt = rj54n1_s_fmt, - .g_mbus_fmt = rj54n1_g_fmt, - .try_mbus_fmt = rj54n1_try_fmt, - .enum_mbus_fmt = rj54n1_enum_fmt, .g_crop = rj54n1_g_crop, .s_crop = rj54n1_s_crop, .cropcap = rj54n1_cropcap, @@ -1260,9 +1253,16 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_mbus_config = rj54n1_s_mbus_config, }; +static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { + .enum_mbus_code = rj54n1_enum_mbus_code, + .get_fmt = rj54n1_get_fmt, + .set_fmt = rj54n1_set_fmt, +}; + static struct v4l2_subdev_ops rj54n1_subdev_ops = { .core = &rj54n1_subdev_core_ops, .video = &rj54n1_subdev_video_ops, + .pad = &rj54n1_subdev_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/soc_camera/tw9910.c b/kernel/drivers/media/i2c/soc_camera/tw9910.c index 9b853215d..e939c24bf 100644 --- a/kernel/drivers/media/i2c/soc_camera/tw9910.c +++ b/kernel/drivers/media/i2c/soc_camera/tw9910.c @@ -510,13 +510,39 @@ static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); + const unsigned hact = 720; + const unsigned hdelay = 15; + unsigned vact; + unsigned vdelay; + int ret; if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) return -EINVAL; priv->norm = norm; + if (norm & V4L2_STD_525_60) { + vact = 240; + vdelay = 18; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); + } else { + vact = 288; + vdelay = 24; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); + } + if (!ret) + ret = i2c_smbus_write_byte_data(client, CROP_HI, + ((vdelay >> 2) & 0xc0) | + ((vact >> 4) & 0x30) | + ((hdelay >> 6) & 0x0c) | + ((hact >> 8) & 0x03)); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VDELAY_LO, + vdelay & 0xff); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, + vact & 0xff); - return 0; + return ret; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -691,12 +717,17 @@ static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int tw9910_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int tw9910_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); + if (format->pad) + return -EINVAL; + if (!priv->scale) { priv->scale = tw9910_select_norm(priv->norm, 640, 480); if (!priv->scale) @@ -706,7 +737,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, mf->width = priv->scale->width; mf->height = priv->scale->height; mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; mf->field = V4L2_FIELD_INTERLACED_BT; return 0; @@ -727,7 +758,7 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) return -EINVAL; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ret = tw9910_set_frame(sd, &width, &height); if (!ret) { @@ -737,13 +768,18 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, return ret; } -static int tw9910_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int tw9910_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); const struct tw9910_scale_ctrl *scale; + if (format->pad) + return -EINVAL; + if (V4L2_FIELD_ANY == mf->field) { mf->field = V4L2_FIELD_INTERLACED_BT; } else if (V4L2_FIELD_INTERLACED_BT != mf->field) { @@ -752,7 +788,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, } mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; /* * select suitable norm @@ -764,6 +800,9 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, mf->width = scale->width; mf->height = scale->height; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return tw9910_s_fmt(sd, mf); + cfg->try_fmt = *mf; return 0; } @@ -807,6 +846,7 @@ static int tw9910_video_probe(struct i2c_client *client) "tw9910 Product ID %0x:%0x\n", id, priv->revision); priv->norm = V4L2_STD_NTSC; + priv->scale = &tw9910_ntsc_scales[0]; done: tw9910_s_power(&priv->subdev, 0); @@ -821,13 +861,14 @@ static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { .s_power = tw9910_s_power, }; -static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + if (code->pad || code->index) return -EINVAL; - *code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_2X8; return 0; } @@ -880,20 +921,23 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .s_std = tw9910_s_std, .g_std = tw9910_g_std, .s_stream = tw9910_s_stream, - .g_mbus_fmt = tw9910_g_fmt, - .s_mbus_fmt = tw9910_s_fmt, - .try_mbus_fmt = tw9910_try_fmt, .cropcap = tw9910_cropcap, .g_crop = tw9910_g_crop, - .enum_mbus_fmt = tw9910_enum_fmt, .g_mbus_config = tw9910_g_mbus_config, .s_mbus_config = tw9910_s_mbus_config, .g_tvnorms = tw9910_g_tvnorms, }; +static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { + .enum_mbus_code = tw9910_enum_mbus_code, + .get_fmt = tw9910_get_fmt, + .set_fmt = tw9910_set_fmt, +}; + static struct v4l2_subdev_ops tw9910_subdev_ops = { .core = &tw9910_subdev_core_ops, .video = &tw9910_subdev_video_ops, + .pad = &tw9910_subdev_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/sony-btf-mpx.c b/kernel/drivers/media/i2c/sony-btf-mpx.c index 1da8004f5..6b1a04ffa 100644 --- a/kernel/drivers/media/i2c/sony-btf-mpx.c +++ b/kernel/drivers/media/i2c/sony-btf-mpx.c @@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); static struct i2c_driver sony_btf_mpx_driver = { .driver = { - .owner = THIS_MODULE, .name = "sony-btf-mpx", }, .probe = sony_btf_mpx_probe, diff --git a/kernel/drivers/media/i2c/sr030pc30.c b/kernel/drivers/media/i2c/sr030pc30.c index 10c735c3a..b04c09dd4 100644 --- a/kernel/drivers/media/i2c/sr030pc30.c +++ b/kernel/drivers/media/i2c/sr030pc30.c @@ -471,30 +471,32 @@ static int sr030pc30_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int sr030pc30_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) + if (!code || code->pad || + code->index >= ARRAY_SIZE(sr030pc30_formats)) return -EINVAL; - *code = sr030pc30_formats[index].code; + code->code = sr030pc30_formats[code->index].code; return 0; } -static int sr030pc30_g_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int sr030pc30_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *mf; struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - if (!mf) + if (!format || format->pad) return -EINVAL; - if (!info->curr_win || !info->curr_fmt) { - ret = sr030pc30_set_params(sd); - if (ret) - return ret; - } + mf = &format->format; + + if (!info->curr_win || !info->curr_fmt) + return -EINVAL; mf->width = info->curr_win->width; mf->height = info->curr_win->height; @@ -523,25 +525,28 @@ static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, } /* Return nearest media bus frame format. */ -static int sr030pc30_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int sr030pc30_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { - if (!sd || !mf) - return -EINVAL; + struct sr030pc30_info *info = sd ? to_sr030pc30(sd) : NULL; + const struct sr030pc30_format *fmt; + struct v4l2_mbus_framefmt *mf; - try_fmt(sd, mf); - return 0; -} - -static int sr030pc30_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); + if (!sd || !format) + return -EINVAL; - if (!sd || !mf) + mf = &format->format; + if (format->pad) return -EINVAL; - info->curr_fmt = try_fmt(sd, mf); + fmt = try_fmt(sd, mf); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + info->curr_fmt = fmt; return sr030pc30_set_params(sd); } @@ -627,25 +632,17 @@ static const struct v4l2_ctrl_ops sr030pc30_ctrl_ops = { static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { .s_power = sr030pc30_s_power, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; -static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { - .g_mbus_fmt = sr030pc30_g_fmt, - .s_mbus_fmt = sr030pc30_s_fmt, - .try_mbus_fmt = sr030pc30_try_fmt, - .enum_mbus_fmt = sr030pc30_enum_fmt, +static const struct v4l2_subdev_pad_ops sr030pc30_pad_ops = { + .enum_mbus_code = sr030pc30_enum_mbus_code, + .get_fmt = sr030pc30_get_fmt, + .set_fmt = sr030pc30_set_fmt, }; static const struct v4l2_subdev_ops sr030pc30_ops = { .core = &sr030pc30_core_ops, - .video = &sr030pc30_video_ops, + .pad = &sr030pc30_pad_ops, }; /* diff --git a/kernel/drivers/media/i2c/tc358743.c b/kernel/drivers/media/i2c/tc358743.c new file mode 100644 index 000000000..9ef5baaf8 --- /dev/null +++ b/kernel/drivers/media/i2c/tc358743.c @@ -0,0 +1,1979 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tc358743_regs.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-3)"); + +MODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver"); +MODULE_AUTHOR("Ramakrishnan Muthukrishnan "); +MODULE_AUTHOR("Mikhail Khelik "); +MODULE_AUTHOR("Mats Randgaard "); +MODULE_LICENSE("GPL"); + +#define EDID_NUM_BLOCKS_MAX 8 +#define EDID_BLOCK_SIZE 128 + +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2) + +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 */ + .reserved = { 0 }, + /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM) +}; + +struct tc358743_state { + struct tc358743_platform_data pdata; + struct v4l2_of_bus_mipi_csi2 bus; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + struct i2c_client *i2c_client; + /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ + struct mutex confctl_mutex; + + /* controls */ + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *audio_sampling_rate_ctrl; + struct v4l2_ctrl *audio_present_ctrl; + + /* work queues */ + struct workqueue_struct *work_queues; + struct delayed_work delayed_work_enable_hotplug; + + /* edid */ + u8 edid_blocks_written; + + /* used by i2c_wr() */ + u8 wr_data[MAX_XFER_SIZE]; + + struct v4l2_dv_timings timings; + u32 mbus_fmt_code; + + struct gpio_desc *reset_gpio; +}; + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd, + bool cable_connected); +static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd); + +static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tc358743_state, sd); +} + +/* --------------- I2C --------------- */ + +static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + int err; + u8 buf[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = n, + .buf = values, + }, + }; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) { + v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + } +} + +static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + u8 *data = state->wr_data; + int err, i; + struct i2c_msg msg; + + if ((2 + n) > sizeof(state->wr_data)) + v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", + reg, 2 + n); + + msg.addr = client->addr; + msg.buf = data; + msg.len = 2 + n; + msg.flags = 0; + + data[0] = reg >> 8; + data[1] = reg & 0xff; + + for (i = 0; i < n; i++) + data[2 + i] = values[i]; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err != 1) { + v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + return; + } + + if (debug < 3) + return; + + switch (n) { + case 1: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x", + reg, data[2]); + break; + case 2: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", + reg, data[3], data[2]); + break; + case 4: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", + reg, data[5], data[4], data[3], data[2]); + break; + default: + v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", + n, reg); + } +} + +static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +{ + u8 val; + + i2c_rd(sd, reg, &val, 1); + + return val; +} + +static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) +{ + i2c_wr(sd, reg, &val, 1); +} + +static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, + u8 mask, u8 val) +{ + i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); +} + +static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) +{ + u16 val; + + i2c_rd(sd, reg, (u8 *)&val, 2); + + return val; +} + +static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 2); +} + +static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) +{ + i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); +} + +static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) +{ + u32 val; + + i2c_rd(sd, reg, (u8 *)&val, 4); + + return val; +} + +static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 4); +} + +/* --------------- STATUS --------------- */ + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; +} + +static inline bool tx_5v_power_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; +} + +static inline bool no_signal(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); +} + +static inline bool no_sync(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); +} + +static inline bool audio_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; +} + +static int get_audio_sampling_rate(struct v4l2_subdev *sd) +{ + static const int code_to_rate[] = { + 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, + 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 + }; + + /* Register FS_SET is not cleared when the cable is disconnected */ + if (no_signal(sd)) + return 0; + + return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; +} + +static unsigned tc358743_num_csi_lanes_in_use(struct v4l2_subdev *sd) +{ + return ((i2c_rd32(sd, CSI_CONTROL) & MASK_NOL) >> 1) + 1; +} + +/* --------------- TIMINGS --------------- */ + +static inline unsigned fps(const struct v4l2_bt_timings *t) +{ + if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) + return 0; + + return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, + V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); +} + +static int tc358743_get_detected_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + unsigned width, height, frame_width, frame_height, frame_interval, fps; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + + if (no_signal(sd)) { + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return -ENOLINK; + } + if (no_sync(sd)) { + v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); + return -ENOLCK; + } + + timings->type = V4L2_DV_BT_656_1120; + bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + + width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_H_LO); + height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_V_LO); + frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + + i2c_rd8(sd, H_SIZE_LO); + frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + + i2c_rd8(sd, V_SIZE_LO)) / 2; + /* frame interval in milliseconds * 10 + * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ + frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + + i2c_rd8(sd, FV_CNT_LO); + fps = (frame_interval > 0) ? + DIV_ROUND_CLOSEST(10000, frame_interval) : 0; + + bt->width = width; + bt->height = height; + bt->vsync = frame_height - height; + bt->hsync = frame_width - width; + bt->pixelclock = frame_width * frame_height * fps; + if (bt->interlaced == V4L2_DV_INTERLACED) { + bt->height *= 2; + bt->il_vsync = bt->vsync + 1; + bt->pixelclock /= 2; + } + + return 0; +} + +/* --------------- HOTPLUG / HDCP / EDID --------------- */ + +static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tc358743_state *state = container_of(dwork, + struct tc358743_state, delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); +} + +static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) +{ + v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? + "enable" : "disable"); + + i2c_wr8_and_or(sd, HDCP_REG1, + ~(MASK_AUTH_UNAUTH_SEL | MASK_AUTH_UNAUTH), + MASK_AUTH_UNAUTH_SEL_16_FRAMES | MASK_AUTH_UNAUTH_AUTO); + + i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, + SET_AUTO_P3_RESET_FRAMES(0x0f)); + + /* HDCP is disabled by configuring the receiver as HDCP repeater. The + * repeater mode require software support to work, so HDCP + * authentication will fail. + */ + i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, enable ? KEY_RD_CMD : 0); + i2c_wr8_and_or(sd, HDCP_MODE, ~(MASK_AUTO_CLR | MASK_MODE_RST_TN), + enable ? (MASK_AUTO_CLR | MASK_MODE_RST_TN) : 0); + + /* Apple MacBook Pro gen.8 has a bug that makes it freeze every fifth + * second when HDCP is disabled, but the MAX_EXCED bit is handled + * correctly and HDCP is disabled on the HDMI output. + */ + i2c_wr8_and_or(sd, BSTATUS1, ~MASK_MAX_EXCED, + enable ? 0 : MASK_MAX_EXCED); + i2c_wr8_and_or(sd, BCAPS, ~(MASK_REPEATER | MASK_READY), + enable ? 0 : MASK_REPEATER | MASK_READY); +} + +static void tc358743_disable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + + /* DDC access to EDID is also disabled when hotplug is disabled. See + * register DDC_CTL */ + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); +} + +static void tc358743_enable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + if (state->edid_blocks_written == 0) { + v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); + return; + } + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when + * hotplug is enabled. See register DDC_CTL */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 10); + + tc358743_enable_interrupts(sd, true); + tc358743_s_ctrl_detect_tx_5v(sd); +} + +static void tc358743_erase_bksv(struct v4l2_subdev *sd) +{ + int i; + + for (i = 0; i < 5; i++) + i2c_wr8(sd, BKSV + i, 0); +} + +/* --------------- AVI infoframe --------------- */ + +static void print_avi_infoframe(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + union hdmi_infoframe frame; + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + + if (!is_hdmi(sd)) { + v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); + return; + } + + i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); + + if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); + return; + } + + hdmi_infoframe_log(KERN_INFO, dev, &frame); +} + +/* --------------- CTRLS --------------- */ + +static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, + tx_5v_power_present(sd)); +} + +static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, + get_audio_sampling_rate(sd)); +} + +static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, + audio_present(sd)); +} + +static int tc358743_update_controls(struct v4l2_subdev *sd) +{ + int ret = 0; + + ret |= tc358743_s_ctrl_detect_tx_5v(sd); + ret |= tc358743_s_ctrl_audio_sampling_rate(sd); + ret |= tc358743_s_ctrl_audio_present(sd); + + return ret; +} + +/* --------------- INIT --------------- */ + +static void tc358743_reset_phy(struct v4l2_subdev *sd) +{ + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); +} + +static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) +{ + u16 sysctl = i2c_rd16(sd, SYSCTL); + + i2c_wr16(sd, SYSCTL, sysctl | mask); + i2c_wr16(sd, SYSCTL, sysctl & ~mask); +} + +static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) +{ + i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, + enable ? MASK_SLEEP : 0); +} + +static inline void enable_stream(struct v4l2_subdev *sd, bool enable) +{ + struct tc358743_state *state = to_state(sd); + + v4l2_dbg(3, debug, sd, "%s: %sable\n", + __func__, enable ? "en" : "dis"); + + if (enable) { + /* It is critical for CSI receiver to see lane transition + * LP11->HS. Set to non-continuous mode to enable clock lane + * LP11 state. */ + i2c_wr32(sd, TXOPTIONCNTRL, 0); + /* Set to continuous mode to trigger LP11->HS transition */ + i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + /* Unmute video */ + i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); + } else { + /* Mute video so that all data lanes go to LSP11 state. + * No data is output to CSI Tx block. */ + i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); + } + + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), + enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); + mutex_unlock(&state->confctl_mutex); +} + +static void tc358743_set_pll(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u16 pllctl0 = i2c_rd16(sd, PLLCTL0); + u16 pllctl1 = i2c_rd16(sd, PLLCTL1); + u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | + SET_PLL_FBD(pdata->pll_fbd); + u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Only rewrite when needed (new value or disabled), since rewriting + * triggers another format change event. */ + if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { + u16 pll_frs; + + if (hsck > 500000000) + pll_frs = 0x0; + else if (hsck > 250000000) + pll_frs = 0x1; + else if (hsck > 125000000) + pll_frs = 0x2; + else + pll_frs = 0x3; + + v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); + tc358743_sleep_mode(sd, true); + i2c_wr16(sd, PLLCTL0, pllctl0_new); + i2c_wr16_and_or(sd, PLLCTL1, + ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), + (SET_PLL_FRS(pll_frs) | MASK_RESETB | + MASK_PLL_EN)); + udelay(10); /* REF_02, Sheet "Source HDMI" */ + i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); + tc358743_sleep_mode(sd, false); + } +} + +static void tc358743_set_ref_clk(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u32 sys_freq; + u32 lockdet_ref; + u16 fh_min; + u16 fh_max; + + BUG_ON(!(pdata->refclk_hz == 26000000 || + pdata->refclk_hz == 27000000 || + pdata->refclk_hz == 42000000)); + + sys_freq = pdata->refclk_hz / 10000; + i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); + i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); + + i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, + (pdata->refclk_hz == 42000000) ? + MASK_PHY_SYSCLK_IND : 0x0); + + fh_min = pdata->refclk_hz / 100000; + i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); + i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); + + fh_max = (fh_min * 66) / 10; + i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); + i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); + + lockdet_ref = pdata->refclk_hz / 100; + i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); + i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); + i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); + + i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, + (pdata->refclk_hz == 27000000) ? + MASK_NCO_F0_MOD_27MHZ : 0x0); +} + +static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + switch (state->mbus_fmt_code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + MASK_SEL422 | MASK_VOUT_422FIL_100); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_601_YCBCR_LIMITED); + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, + MASK_YCBCRFMT_422_8_BIT); + mutex_unlock(&state->confctl_mutex); + break; + case MEDIA_BUS_FMT_RGB888_1X24: + v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + 0x00); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_RGB_FULL); + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); + mutex_unlock(&state->confctl_mutex); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", + __func__, state->mbus_fmt_code); + } +} + +static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_bt_timings *bt = &state->timings.bt; + struct tc358743_platform_data *pdata = &state->pdata; + u32 bits_pr_pixel = + (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; + u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; + u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; + + return DIV_ROUND_UP(bps, bps_pr_lane); +} + +static void tc358743_set_csi(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + unsigned lanes = tc358743_num_csi_lanes_needed(sd); + + v4l2_dbg(3, debug, sd, "%s:\n", __func__); + + tc358743_reset(sd, MASK_CTXRST); + + if (lanes < 1) + i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); + if (lanes < 1) + i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); + if (lanes < 2) + i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); + if (lanes < 3) + i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); + if (lanes < 4) + i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); + + i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); + i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); + i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); + i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); + i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); + i2c_wr32(sd, TWAKEUP, pdata->twakeup); + i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); + i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); + i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); + + i2c_wr32(sd, HSTXVREGEN, + ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | + ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | + ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | + ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | + ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); + + i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); + i2c_wr32(sd, STARTCNTRL, MASK_START); + i2c_wr32(sd, CSI_START, MASK_STRT); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_CONTROL | + MASK_CSI_MODE | + MASK_TXHSMD | + ((lanes == 4) ? MASK_NOL_4 : + (lanes == 3) ? MASK_NOL_3 : + (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | + MASK_WCER | MASK_INER); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | + MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); +} + +static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* Default settings from REF_02, sheet "Source HDMI" + * and custom settings as platform data */ + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); + i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | + SET_FREQ_RANGE_MODE_CYCLES(1)); + i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, + (pdata->hdmi_phy_auto_reset_tmds_detected ? + MASK_PHY_AUTO_RST2 : 0) | + (pdata->hdmi_phy_auto_reset_tmds_in_range ? + MASK_PHY_AUTO_RST3 : 0) | + (pdata->hdmi_phy_auto_reset_tmds_valid ? + MASK_PHY_AUTO_RST4 : 0)); + i2c_wr8(sd, PHY_BIAS, 0x40); + i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); + i2c_wr8(sd, AVM_CTL, 45); + i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, + pdata->hdmi_detection_delay << 4); + i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), + (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? + MASK_H_PI_RST : 0) | + (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? + MASK_V_PI_RST : 0)); + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); +} + +static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, FORCE_MUTE, 0x00); + i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | + MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | + MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); + i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); + i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); + i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); + i2c_wr8(sd, FS_MUTE, 0x00); + i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); + i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); + i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); + i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); + i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); + i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); + + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | + MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); + mutex_unlock(&state->confctl_mutex); +} + +static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) +{ + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | + MASK_ACP_INT_MODE | MASK_VS_INT_MODE | + MASK_SPD_INT_MODE | MASK_MS_INT_MODE | + MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); + i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); + i2c_wr8(sd, NO_PKT_CLR, 0x53); + i2c_wr8(sd, ERR_PK_LIMIT, 0x01); + i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); + i2c_wr8(sd, NO_GDB_LIMIT, 0x10); +} + +static void tc358743_initial_setup(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* CEC and IR are not supported by this driver */ + i2c_wr16_and_or(sd, SYSCTL, ~(MASK_CECRST | MASK_IRRST), + (MASK_CECRST | MASK_IRRST)); + + tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); + tc358743_sleep_mode(sd, false); + + i2c_wr16(sd, FIFOCTL, pdata->fifo_level); + + tc358743_set_ref_clk(sd); + + i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, + pdata->ddc5v_delay & MASK_DDC5V_MODE); + i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); + + tc358743_set_hdmi_phy(sd); + tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); + tc358743_set_hdmi_audio(sd); + tc358743_set_hdmi_info_frame_mode(sd); + + /* All CE and IT formats are detected as RGB full range in DVI mode */ + i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); + + i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, + MASK_VOUTCOLORMODE_AUTO); + i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); +} + +/* --------------- IRQ --------------- */ + +static void tc358743_format_change(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_dv_timings timings; + const struct v4l2_event tc358743_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + if (tc358743_get_detected_timings(sd, &timings)) { + enable_stream(sd, false); + + v4l2_dbg(1, debug, sd, "%s: Format changed. No signal\n", + __func__); + } else { + if (!v4l2_match_dv_timings(&state->timings, &timings, 0)) + enable_stream(sd, false); + + v4l2_print_dv_timings(sd->name, + "tc358743_format_change: Format changed. New format: ", + &timings, false); + } + + if (sd->devnode) + v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); +} + +static void tc358743_init_interrupts(struct v4l2_subdev *sd) +{ + u16 i; + + /* clear interrupt status registers */ + for (i = SYS_INT; i <= KEY_INT; i++) + i2c_wr8(sd, i, 0xff); + + i2c_wr16(sd, INTSTATUS, 0xffff); +} + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd, + bool cable_connected) +{ + v4l2_dbg(2, debug, sd, "%s: cable connected = %d\n", __func__, + cable_connected); + + if (cable_connected) { + i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_DVI_DET | + MASK_M_HDMI_DET) & 0xff); + i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); + i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | + MASK_M_AF_UNLOCK) & 0xff); + i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); + i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); + } else { + i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); + i2c_wr8(sd, CLK_INTM, 0xff); + i2c_wr8(sd, CBIT_INTM, 0xff); + i2c_wr8(sd, AUDIO_INTM, 0xff); + i2c_wr8(sd, MISC_INTM, 0xff); + } +} + +static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); + u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; + + i2c_wr8(sd, AUDIO_INT, audio_int); + + v4l2_dbg(3, debug, sd, "%s: AUDIO_INT = 0x%02x\n", __func__, audio_int); + + tc358743_s_ctrl_audio_sampling_rate(sd); + tc358743_s_ctrl_audio_present(sd); +} + +static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); + + i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); +} + +static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); + u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; + + i2c_wr8(sd, MISC_INT, misc_int); + + v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); + + if (misc_int & MASK_I_SYNC_CHG) { + /* Reset the HDMI PHY to try to trigger proper lock on the + * incoming video format. Erase BKSV to prevent that old keys + * are used when a new source is connected. */ + if (no_sync(sd) || no_signal(sd)) { + tc358743_reset_phy(sd); + tc358743_erase_bksv(sd); + } + + tc358743_format_change(sd); + + misc_int &= ~MASK_I_SYNC_CHG; + if (handled) + *handled = true; + } + + if (misc_int) { + v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", + __func__, misc_int); + } +} + +static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); + u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; + + i2c_wr8(sd, CBIT_INT, cbit_int); + + v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); + + if (cbit_int & MASK_I_CBIT_FS) { + + v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", + __func__); + tc358743_s_ctrl_audio_sampling_rate(sd); + + cbit_int &= ~MASK_I_CBIT_FS; + if (handled) + *handled = true; + } + + if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { + + v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", + __func__); + tc358743_s_ctrl_audio_present(sd); + + cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); + if (handled) + *handled = true; + } + + if (cbit_int) { + v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", + __func__, cbit_int); + } +} + +static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); + u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; + + /* Bit 7 and bit 6 are set even when they are masked */ + i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); + + v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); + + if (clk_int & (MASK_I_IN_DE_CHG)) { + + v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", + __func__); + + /* If the source switch to a new resolution with the same pixel + * frequency as the existing (e.g. 1080p25 -> 720p50), the + * I_SYNC_CHG interrupt is not always triggered, while the + * I_IN_DE_CHG interrupt seems to work fine. Format change + * notifications are only sent when the signal is stable to + * reduce the number of notifications. */ + if (!no_signal(sd) && !no_sync(sd)) + tc358743_format_change(sd); + + clk_int &= ~(MASK_I_IN_DE_CHG); + if (handled) + *handled = true; + } + + if (clk_int) { + v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", + __func__, clk_int); + } +} + +static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + struct tc358743_state *state = to_state(sd); + u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); + u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; + + i2c_wr8(sd, SYS_INT, sys_int); + + v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); + + if (sys_int & MASK_I_DDC) { + bool tx_5v = tx_5v_power_present(sd); + + v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", + __func__, tx_5v ? "yes" : "no"); + + if (tx_5v) { + tc358743_enable_edid(sd); + } else { + tc358743_enable_interrupts(sd, false); + tc358743_disable_edid(sd); + memset(&state->timings, 0, sizeof(state->timings)); + tc358743_erase_bksv(sd); + tc358743_update_controls(sd); + } + + sys_int &= ~MASK_I_DDC; + if (handled) + *handled = true; + } + + if (sys_int & MASK_I_DVI) { + v4l2_dbg(1, debug, sd, "%s: HDMI->DVI change detected\n", + __func__); + + /* Reset the HDMI PHY to try to trigger proper lock on the + * incoming video format. Erase BKSV to prevent that old keys + * are used when a new source is connected. */ + if (no_sync(sd) || no_signal(sd)) { + tc358743_reset_phy(sd); + tc358743_erase_bksv(sd); + } + + sys_int &= ~MASK_I_DVI; + if (handled) + *handled = true; + } + + if (sys_int & MASK_I_HDMI) { + v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", + __func__); + + /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ + i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); + + sys_int &= ~MASK_I_HDMI; + if (handled) + *handled = true; + } + + if (sys_int) { + v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", + __func__, sys_int); + } +} + +/* --------------- CORE OPS --------------- */ + +static int tc358743_log_status(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_dv_timings timings; + uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); + uint16_t sysctl = i2c_rd16(sd, SYSCTL); + u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); + const int deep_color_mode[4] = { 8, 10, 12, 16 }; + static const char * const input_color_space[] = { + "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", + "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", + "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip ID: 0x%02x\n", + (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); + v4l2_info(sd, "Chip revision: 0x%02x\n", + i2c_rd16(sd, CHIPID) & MASK_REVID); + v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", + !!(sysctl & MASK_IRRST), + !!(sysctl & MASK_CECRST), + !!(sysctl & MASK_CTXRST), + !!(sysctl & MASK_HDMIRST)); + v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); + v4l2_info(sd, "Cable detected (+5V power): %s\n", + hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); + v4l2_info(sd, "DDC lines enabled: %s\n", + (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? + "yes" : "no"); + v4l2_info(sd, "Hotplug enabled: %s\n", + (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? + "yes" : "no"); + v4l2_info(sd, "CEC enabled: %s\n", + (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); + v4l2_info(sd, "-----Signal status-----\n"); + v4l2_info(sd, "TMDS signal detected: %s\n", + hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); + v4l2_info(sd, "Stable sync signal: %s\n", + hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); + v4l2_info(sd, "PHY PLL locked: %s\n", + hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); + v4l2_info(sd, "PHY DE detected: %s\n", + hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); + + if (tc358743_get_detected_timings(sd, &timings)) { + v4l2_info(sd, "No video detected\n"); + } else { + v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, + true); + } + v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, + true); + + v4l2_info(sd, "-----CSI-TX status-----\n"); + v4l2_info(sd, "Lanes needed: %d\n", + tc358743_num_csi_lanes_needed(sd)); + v4l2_info(sd, "Lanes in use: %d\n", + tc358743_num_csi_lanes_in_use(sd)); + v4l2_info(sd, "Waiting for particular sync signal: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? + "yes" : "no"); + v4l2_info(sd, "Transmit mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? + "yes" : "no"); + v4l2_info(sd, "Receive mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? + "yes" : "no"); + v4l2_info(sd, "Stopped: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? + "yes" : "no"); + v4l2_info(sd, "Color space: %s\n", + state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? + "YCbCr 422 16-bit" : + state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? + "RGB 888 24-bit" : "Unsupported"); + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", + hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); + v4l2_info(sd, "Input color space: %s %s range\n", + input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], + (vi_status3 & MASK_LIMITED) ? "limited" : "full"); + if (!is_hdmi(sd)) + return 0; + v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : + "off"); + v4l2_info(sd, "Deep color mode: %d-bits per channel\n", + deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & + MASK_S_DEEPCOLOR) >> 2]); + print_avi_infoframe(sd); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void tc358743_print_register_map(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); + v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); + v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); + v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); + v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); + v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); + v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); + v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); + v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); + v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); + v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); + v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); + v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); + v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); + v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); + v4l2_info(sd, "0x9300- : Reserved\n"); +} + +static int tc358743_get_reg_size(u16 address) +{ + /* REF_01 p. 66-72 */ + if (address <= 0x00ff) + return 2; + else if ((address >= 0x0100) && (address <= 0x06FF)) + return 4; + else if ((address >= 0x0700) && (address <= 0x84ff)) + return 2; + else + return 1; +} + +static int tc358743_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + reg->size = tc358743_get_reg_size(reg->reg); + + i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + + return 0; +} + +static int tc358743_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + /* It should not be possible for the user to enable HDCP with a simple + * v4l2-dbg command. + * + * DO NOT REMOVE THIS unless all other issues with HDCP have been + * resolved. + */ + if (reg->reg == HDCP_MODE || + reg->reg == HDCP_REG1 || + reg->reg == HDCP_REG2 || + reg->reg == HDCP_REG3 || + reg->reg == BCAPS) + return 0; + + i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, + tc358743_get_reg_size(reg->reg)); + + return 0; +} +#endif + +static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + u16 intstatus = i2c_rd16(sd, INTSTATUS); + + v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); + + if (intstatus & MASK_HDMI_INT) { + u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); + u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); + + if (hdmi_int0 & MASK_I_MISC) + tc358743_hdmi_misc_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CBIT) + tc358743_hdmi_cbit_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CLK) + tc358743_hdmi_clk_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_SYS) + tc358743_hdmi_sys_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_AUD) + tc358743_hdmi_audio_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); + intstatus &= ~MASK_HDMI_INT; + } + + if (intstatus & MASK_CSI_INT) { + u32 csi_int = i2c_rd32(sd, CSI_INT); + + if (csi_int & MASK_INTER) + tc358743_csi_err_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); + intstatus &= ~MASK_CSI_INT; + } + + intstatus = i2c_rd16(sd, INTSTATUS); + if (intstatus) { + v4l2_dbg(1, debug, sd, + "%s: Unhandled IntStatus interrupts: 0x%02x\n", + __func__, intstatus); + } + + return 0; +} + +static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) +{ + struct tc358743_state *state = dev_id; + bool handled; + + tc358743_isr(&state->sd, 0, &handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + +/* --------------- VIDEO OPS --------------- */ + +static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + *status = 0; + *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; + *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; + + v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); + + return 0; +} + +static int tc358743_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + + if (!timings) + return -EINVAL; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", + timings, false); + + if (v4l2_match_dv_timings(&state->timings, timings, 0)) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + state->timings = *timings; + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + + return 0; +} + +static int tc358743_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + + *timings = state->timings; + + return 0; +} + +static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + if (timings->pad != 0) + return -EINVAL; + + return v4l2_enum_dv_timings_cap(timings, + &tc358743_timings_cap, NULL, NULL); +} + +static int tc358743_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int ret; + + ret = tc358743_get_detected_timings(sd, timings); + if (ret) + return ret; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", + timings, false); + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + return 0; +} + +static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + if (cap->pad != 0) + return -EINVAL; + + *cap = tc358743_timings_cap; + + return 0; +} + +static int tc358743_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2; + + /* Support for non-continuous CSI-2 clock is missing in the driver */ + cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + switch (tc358743_num_csi_lanes_in_use(sd)) { + case 1: + cfg->flags |= V4L2_MBUS_CSI2_1_LANE; + break; + case 2: + cfg->flags |= V4L2_MBUS_CSI2_2_LANE; + break; + case 3: + cfg->flags |= V4L2_MBUS_CSI2_3_LANE; + break; + case 4: + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +{ + enable_stream(sd, enable); + + return 0; +} + +/* --------------- PAD OPS --------------- */ + +static int tc358743_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + u8 vi_rep = i2c_rd8(sd, VI_REP); + + if (format->pad != 0) + return -EINVAL; + + format->format.code = state->mbus_fmt_code; + format->format.width = state->timings.bt.width; + format->format.height = state->timings.bt.height; + format->format.field = V4L2_FIELD_NONE; + + switch (vi_rep & MASK_VOUT_COLOR_SEL) { + case MASK_VOUT_COLOR_RGB_FULL: + case MASK_VOUT_COLOR_RGB_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_SRGB; + break; + case MASK_VOUT_COLOR_601_YCBCR_LIMITED: + case MASK_VOUT_COLOR_601_YCBCR_FULL: + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + case MASK_VOUT_COLOR_709_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_REC709; + break; + default: + format->format.colorspace = 0; + break; + } + + return 0; +} + +static int tc358743_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + + u32 code = format->format.code; /* is overwritten by get_fmt */ + int ret = tc358743_get_fmt(sd, cfg, format); + + format->format.code = code; + + if (ret) + return ret; + + switch (code) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + break; + default: + return -EINVAL; + } + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + state->mbus_fmt_code = format->format.code; + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + tc358743_set_csi_color_space(sd); + + return 0; +} + +static int tc358743_g_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + + if (edid->pad != 0) + return -EINVAL; + + if (edid->start_block == 0 && edid->blocks == 0) { + edid->blocks = state->edid_blocks_written; + return 0; + } + + if (state->edid_blocks_written == 0) + return -ENODATA; + + if (edid->start_block >= state->edid_blocks_written || + edid->blocks == 0) + return -EINVAL; + + if (edid->start_block + edid->blocks > state->edid_blocks_written) + edid->blocks = state->edid_blocks_written - edid->start_block; + + i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, + edid->blocks * EDID_BLOCK_SIZE); + + return 0; +} + +static int tc358743_s_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; + + v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", + __func__, edid->pad, edid->start_block, edid->blocks); + + if (edid->pad != 0) + return -EINVAL; + + if (edid->start_block != 0) + return -EINVAL; + + if (edid->blocks > EDID_NUM_BLOCKS_MAX) { + edid->blocks = EDID_NUM_BLOCKS_MAX; + return -E2BIG; + } + + tc358743_disable_edid(sd); + + i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); + i2c_wr8(sd, EDID_LEN2, edid_len >> 8); + + if (edid->blocks == 0) { + state->edid_blocks_written = 0; + return 0; + } + + i2c_wr(sd, EDID_RAM, edid->edid, edid_len); + + state->edid_blocks_written = edid->blocks; + + if (tx_5v_power_present(sd)) + tc358743_enable_edid(sd); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tc358743_core_ops = { + .log_status = tc358743_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tc358743_g_register, + .s_register = tc358743_s_register, +#endif + .interrupt_service_routine = tc358743_isr, + .subscribe_event = tc358743_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops tc358743_video_ops = { + .g_input_status = tc358743_g_input_status, + .s_dv_timings = tc358743_s_dv_timings, + .g_dv_timings = tc358743_g_dv_timings, + .query_dv_timings = tc358743_query_dv_timings, + .g_mbus_config = tc358743_g_mbus_config, + .s_stream = tc358743_s_stream, +}; + +static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { + .set_fmt = tc358743_set_fmt, + .get_fmt = tc358743_get_fmt, + .get_edid = tc358743_g_edid, + .set_edid = tc358743_s_edid, + .enum_dv_timings = tc358743_enum_dv_timings, + .dv_timings_cap = tc358743_dv_timings_cap, +}; + +static const struct v4l2_subdev_ops tc358743_ops = { + .core = &tc358743_core_ops, + .video = &tc358743_video_ops, + .pad = &tc358743_pad_ops, +}; + +/* --------------- CUSTOM CTRLS --------------- */ + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { + .id = TC358743_CID_AUDIO_SAMPLING_RATE, + .name = "Audio sampling rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 768000, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { + .id = TC358743_CID_AUDIO_PRESENT, + .name = "Audio present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +/* --------------- PROBE / REMOVE --------------- */ + +#ifdef CONFIG_OF +static void tc358743_gpio_reset(struct tc358743_state *state) +{ + usleep_range(5000, 10000); + gpiod_set_value(state->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(state->reset_gpio, 0); + msleep(20); +} + +static int tc358743_probe_of(struct tc358743_state *state) +{ + struct device *dev = &state->i2c_client->dev; + struct v4l2_of_endpoint *endpoint; + struct device_node *ep; + struct clk *refclk; + u32 bps_pr_lane; + int ret = -EINVAL; + + refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(refclk)) { + if (PTR_ERR(refclk) != -EPROBE_DEFER) + dev_err(dev, "failed to get refclk: %ld\n", + PTR_ERR(refclk)); + return PTR_ERR(refclk); + } + + ep = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!ep) { + dev_err(dev, "missing endpoint node\n"); + return -EINVAL; + } + + endpoint = v4l2_of_alloc_parse_endpoint(ep); + if (IS_ERR(endpoint)) { + dev_err(dev, "failed to parse endpoint\n"); + return PTR_ERR(endpoint); + } + + if (endpoint->bus_type != V4L2_MBUS_CSI2 || + endpoint->bus.mipi_csi2.num_data_lanes == 0 || + endpoint->nr_of_link_frequencies == 0) { + dev_err(dev, "missing CSI-2 properties in endpoint\n"); + goto free_endpoint; + } + + state->bus = endpoint->bus.mipi_csi2; + + clk_prepare_enable(refclk); + + state->pdata.refclk_hz = clk_get_rate(refclk); + state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; + state->pdata.enable_hdcp = false; + /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ + state->pdata.fifo_level = 16; + /* + * The PLL input clock is obtained by dividing refclk by pll_prd. + * It must be between 6 MHz and 40 MHz, lower frequency is better. + */ + switch (state->pdata.refclk_hz) { + case 26000000: + case 27000000: + case 42000000: + state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; + break; + default: + dev_err(dev, "unsupported refclk rate: %u Hz\n", + state->pdata.refclk_hz); + goto disable_clk; + } + + /* + * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. + * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. + */ + bps_pr_lane = 2 * endpoint->link_frequencies[0]; + if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { + dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); + goto disable_clk; + } + + /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ + state->pdata.pll_fbd = bps_pr_lane / + state->pdata.refclk_hz * state->pdata.pll_prd; + + /* + * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz + * link frequency). In principle it should be possible to calculate + * them based on link frequency and resolution. + */ + if (bps_pr_lane != 594000000U) + dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); + state->pdata.lineinitcnt = 0xe80; + state->pdata.lptxtimecnt = 0x003; + /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ + state->pdata.tclk_headercnt = 0x1403; + state->pdata.tclk_trailcnt = 0x00; + /* ths-preparecnt: 3, ths-zerocnt: 1 */ + state->pdata.ths_headercnt = 0x0103; + state->pdata.twakeup = 0x4882; + state->pdata.tclk_postcnt = 0x008; + state->pdata.ths_trailcnt = 0x2; + state->pdata.hstxvregcnt = 0; + + state->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(state->reset_gpio)) { + dev_err(dev, "failed to get reset gpio\n"); + ret = PTR_ERR(state->reset_gpio); + goto disable_clk; + } + + if (state->reset_gpio) + tc358743_gpio_reset(state); + + ret = 0; + goto free_endpoint; + +disable_clk: + clk_disable_unprepare(refclk); +free_endpoint: + v4l2_of_free_endpoint(endpoint); + return ret; +} +#else +static inline int tc358743_probe_of(struct tc358743_state *state) +{ + return -ENODEV; +} +#endif + +static int tc358743_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static struct v4l2_dv_timings default_timing = + V4L2_DV_BT_CEA_640X480P59_94; + struct tc358743_state *state; + struct tc358743_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), + GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->i2c_client = client; + + /* platform data */ + if (pdata) { + state->pdata = *pdata; + state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else { + err = tc358743_probe_of(state); + if (err == -ENODEV) + v4l_err(client, "No platform data!\n"); + if (err) + return err; + } + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tc358743_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + + /* i2c access */ + if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { + v4l2_info(sd, "not a TC358743 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } + + /* control handlers */ + v4l2_ctrl_handler_init(&state->hdl, 3); + + /* private controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + + /* custom controls */ + state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_sampling_rate, NULL); + + state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_present, NULL); + + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + err = state->hdl.error; + goto err_hdl; + } + + if (tc358743_update_controls(sd)) { + err = -ENODEV; + goto err_hdl; + } + + /* work queues */ + state->work_queues = create_singlethread_workqueue(client->name); + if (!state->work_queues) { + v4l2_err(sd, "Could not create work queue\n"); + err = -ENOMEM; + goto err_hdl; + } + + state->pad.flags = MEDIA_PAD_FL_SOURCE; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err < 0) + goto err_hdl; + + sd->dev = &client->dev; + err = v4l2_async_register_subdev(sd); + if (err < 0) + goto err_hdl; + + mutex_init(&state->confctl_mutex); + + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, + tc358743_delayed_work_enable_hotplug); + + tc358743_initial_setup(sd); + + 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); + + if (state->i2c_client->irq) { + err = devm_request_threaded_irq(&client->dev, + state->i2c_client->irq, + NULL, tc358743_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "tc358743", state); + if (err) + goto err_work_queues; + } + + tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); + i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); + + err = v4l2_ctrl_handler_setup(sd->ctrl_handler); + if (err) + goto err_work_queues; + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + return 0; + +err_work_queues: + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + mutex_destroy(&state->confctl_mutex); +err_hdl: + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&state->hdl); + return err; +} + +static int tc358743_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tc358743_state *state = to_state(sd); + + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + v4l2_async_unregister_subdev(sd); + v4l2_device_unregister_subdev(sd); + mutex_destroy(&state->confctl_mutex); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&state->hdl); + + return 0; +} + +static struct i2c_device_id tc358743_id[] = { + {"tc358743", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tc358743_id); + +static struct i2c_driver tc358743_driver = { + .driver = { + .name = "tc358743", + }, + .probe = tc358743_probe, + .remove = tc358743_remove, + .id_table = tc358743_id, +}; + +module_i2c_driver(tc358743_driver); diff --git a/kernel/drivers/media/i2c/tc358743_regs.h b/kernel/drivers/media/i2c/tc358743_regs.h new file mode 100644 index 000000000..81f1db558 --- /dev/null +++ b/kernel/drivers/media/i2c/tc358743_regs.h @@ -0,0 +1,681 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + */ + +/* Bit masks has prefix 'MASK_' and options after '_'. */ + +#ifndef __TC358743_REGS_H +#define __TC358743_REGS_H + +#define CHIPID 0x0000 +#define MASK_CHIPID 0xff00 +#define MASK_REVID 0x00ff + +#define SYSCTL 0x0002 +#define MASK_IRRST 0x0800 +#define MASK_CECRST 0x0400 +#define MASK_CTXRST 0x0200 +#define MASK_HDMIRST 0x0100 +#define MASK_SLEEP 0x0001 + +#define CONFCTL 0x0004 +#define MASK_PWRISO 0x8000 +#define MASK_ACLKOPT 0x1000 +#define MASK_AUDCHNUM 0x0c00 +#define MASK_AUDCHNUM_8 0x0000 +#define MASK_AUDCHNUM_6 0x0400 +#define MASK_AUDCHNUM_4 0x0800 +#define MASK_AUDCHNUM_2 0x0c00 +#define MASK_AUDCHSEL 0x0200 +#define MASK_I2SDLYOPT 0x0100 +#define MASK_YCBCRFMT 0x00c0 +#define MASK_YCBCRFMT_444 0x0000 +#define MASK_YCBCRFMT_422_12_BIT 0x0040 +#define MASK_YCBCRFMT_COLORBAR 0x0080 +#define MASK_YCBCRFMT_422_8_BIT 0x00c0 +#define MASK_INFRMEN 0x0020 +#define MASK_AUDOUTSEL 0x0018 +#define MASK_AUDOUTSEL_CSI 0x0000 +#define MASK_AUDOUTSEL_I2S 0x0010 +#define MASK_AUDOUTSEL_TDM 0x0018 +#define MASK_AUTOINDEX 0x0004 +#define MASK_ABUFEN 0x0002 +#define MASK_VBUFEN 0x0001 + +#define FIFOCTL 0x0006 + +#define INTSTATUS 0x0014 +#define MASK_AMUTE_INT 0x0400 +#define MASK_HDMI_INT 0x0200 +#define MASK_CSI_INT 0x0100 +#define MASK_SYS_INT 0x0020 +#define MASK_CEC_EINT 0x0010 +#define MASK_CEC_TINT 0x0008 +#define MASK_CEC_RINT 0x0004 +#define MASK_IR_EINT 0x0002 +#define MASK_IR_DINT 0x0001 + +#define INTMASK 0x0016 +#define MASK_AMUTE_MSK 0x0400 +#define MASK_HDMI_MSK 0x0200 +#define MASK_CSI_MSK 0x0100 +#define MASK_SYS_MSK 0x0020 +#define MASK_CEC_EMSK 0x0010 +#define MASK_CEC_TMSK 0x0008 +#define MASK_CEC_RMSK 0x0004 +#define MASK_IR_EMSK 0x0002 +#define MASK_IR_DMSK 0x0001 + +#define INTFLAG 0x0018 +#define INTSYSSTATUS 0x001A + +#define PLLCTL0 0x0020 +#define MASK_PLL_PRD 0xf000 +#define SET_PLL_PRD(prd) ((((prd) - 1) << 12) &\ + MASK_PLL_PRD) +#define MASK_PLL_FBD 0x01ff +#define SET_PLL_FBD(fbd) (((fbd) - 1) & MASK_PLL_FBD) + +#define PLLCTL1 0x0022 +#define MASK_PLL_FRS 0x0c00 +#define SET_PLL_FRS(frs) (((frs) << 10) & MASK_PLL_FRS) +#define MASK_PLL_LBWS 0x0300 +#define MASK_LFBREN 0x0040 +#define MASK_BYPCKEN 0x0020 +#define MASK_CKEN 0x0010 +#define MASK_RESETB 0x0002 +#define MASK_PLL_EN 0x0001 + +#define CLW_CNTRL 0x0140 +#define MASK_CLW_LANEDISABLE 0x0001 + +#define D0W_CNTRL 0x0144 +#define MASK_D0W_LANEDISABLE 0x0001 + +#define D1W_CNTRL 0x0148 +#define MASK_D1W_LANEDISABLE 0x0001 + +#define D2W_CNTRL 0x014C +#define MASK_D2W_LANEDISABLE 0x0001 + +#define D3W_CNTRL 0x0150 +#define MASK_D3W_LANEDISABLE 0x0001 + +#define STARTCNTRL 0x0204 +#define MASK_START 0x00000001 + +#define LINEINITCNT 0x0210 +#define LPTXTIMECNT 0x0214 +#define TCLK_HEADERCNT 0x0218 +#define TCLK_TRAILCNT 0x021C +#define THS_HEADERCNT 0x0220 +#define TWAKEUP 0x0224 +#define TCLK_POSTCNT 0x0228 +#define THS_TRAILCNT 0x022C +#define HSTXVREGCNT 0x0230 + +#define HSTXVREGEN 0x0234 +#define MASK_D3M_HSTXVREGEN 0x0010 +#define MASK_D2M_HSTXVREGEN 0x0008 +#define MASK_D1M_HSTXVREGEN 0x0004 +#define MASK_D0M_HSTXVREGEN 0x0002 +#define MASK_CLM_HSTXVREGEN 0x0001 + + +#define TXOPTIONCNTRL 0x0238 +#define MASK_CONTCLKMODE 0x00000001 + +#define CSI_CONTROL 0x040C +#define MASK_CSI_MODE 0x8000 +#define MASK_HTXTOEN 0x0400 +#define MASK_TXHSMD 0x0080 +#define MASK_HSCKMD 0x0020 +#define MASK_NOL 0x0006 +#define MASK_NOL_1 0x0000 +#define MASK_NOL_2 0x0002 +#define MASK_NOL_3 0x0004 +#define MASK_NOL_4 0x0006 +#define MASK_EOTDIS 0x0001 + +#define CSI_INT 0x0414 +#define MASK_INTHLT 0x00000008 +#define MASK_INTER 0x00000004 + +#define CSI_INT_ENA 0x0418 +#define MASK_IENHLT 0x00000008 +#define MASK_IENER 0x00000004 + +#define CSI_ERR 0x044C +#define MASK_INER 0x00000200 +#define MASK_WCER 0x00000100 +#define MASK_QUNK 0x00000010 +#define MASK_TXBRK 0x00000002 + +#define CSI_ERR_INTENA 0x0450 +#define CSI_ERR_HALT 0x0454 + +#define CSI_CONFW 0x0500 +#define MASK_MODE 0xe0000000 +#define MASK_MODE_SET 0xa0000000 +#define MASK_MODE_CLEAR 0xc0000000 +#define MASK_ADDRESS 0x1f000000 +#define MASK_ADDRESS_CSI_CONTROL 0x03000000 +#define MASK_ADDRESS_CSI_INT_ENA 0x06000000 +#define MASK_ADDRESS_CSI_ERR_INTENA 0x14000000 +#define MASK_ADDRESS_CSI_ERR_HALT 0x15000000 +#define MASK_DATA 0x0000ffff + +#define CSI_INT_CLR 0x050C +#define MASK_ICRER 0x00000004 + +#define CSI_START 0x0518 +#define MASK_STRT 0x00000001 + +#define CECEN 0x0600 +#define MASK_CECEN 0x0001 + +#define HDMI_INT0 0x8500 +#define MASK_I_KEY 0x80 +#define MASK_I_MISC 0x02 +#define MASK_I_PHYERR 0x01 + +#define HDMI_INT1 0x8501 +#define MASK_I_GBD 0x80 +#define MASK_I_HDCP 0x40 +#define MASK_I_ERR 0x20 +#define MASK_I_AUD 0x10 +#define MASK_I_CBIT 0x08 +#define MASK_I_PACKET 0x04 +#define MASK_I_CLK 0x02 +#define MASK_I_SYS 0x01 + +#define SYS_INT 0x8502 +#define MASK_I_ACR_CTS 0x80 +#define MASK_I_ACRN 0x40 +#define MASK_I_DVI 0x20 +#define MASK_I_HDMI 0x10 +#define MASK_I_NOPMBDET 0x08 +#define MASK_I_DPMBDET 0x04 +#define MASK_I_TMDS 0x02 +#define MASK_I_DDC 0x01 + +#define CLK_INT 0x8503 +#define MASK_I_OUT_H_CHG 0x40 +#define MASK_I_IN_DE_CHG 0x20 +#define MASK_I_IN_HV_CHG 0x10 +#define MASK_I_DC_CHG 0x08 +#define MASK_I_PXCLK_CHG 0x04 +#define MASK_I_PHYCLK_CHG 0x02 +#define MASK_I_TMDSCLK_CHG 0x01 + +#define CBIT_INT 0x8505 +#define MASK_I_AF_LOCK 0x80 +#define MASK_I_AF_UNLOCK 0x40 +#define MASK_I_CBIT_FS 0x02 + +#define AUDIO_INT 0x8506 + +#define ERR_INT 0x8507 +#define MASK_I_EESS_ERR 0x80 + +#define HDCP_INT 0x8508 +#define MASK_I_AVM_SET 0x80 +#define MASK_I_AVM_CLR 0x40 +#define MASK_I_LINKERR 0x20 +#define MASK_I_SHA_END 0x10 +#define MASK_I_R0_END 0x08 +#define MASK_I_KM_END 0x04 +#define MASK_I_AKSV_END 0x02 +#define MASK_I_AN_END 0x01 + +#define MISC_INT 0x850B +#define MASK_I_AS_LAYOUT 0x10 +#define MASK_I_NO_SPD 0x08 +#define MASK_I_NO_VS 0x03 +#define MASK_I_SYNC_CHG 0x02 +#define MASK_I_AUDIO_MUTE 0x01 + +#define KEY_INT 0x850F + +#define SYS_INTM 0x8512 +#define MASK_M_ACR_CTS 0x80 +#define MASK_M_ACR_N 0x40 +#define MASK_M_DVI_DET 0x20 +#define MASK_M_HDMI_DET 0x10 +#define MASK_M_NOPMBDET 0x08 +#define MASK_M_BPMBDET 0x04 +#define MASK_M_TMDS 0x02 +#define MASK_M_DDC 0x01 + +#define CLK_INTM 0x8513 +#define MASK_M_OUT_H_CHG 0x40 +#define MASK_M_IN_DE_CHG 0x20 +#define MASK_M_IN_HV_CHG 0x10 +#define MASK_M_DC_CHG 0x08 +#define MASK_M_PXCLK_CHG 0x04 +#define MASK_M_PHYCLK_CHG 0x02 +#define MASK_M_TMDS_CHG 0x01 + +#define PACKET_INTM 0x8514 + +#define CBIT_INTM 0x8515 +#define MASK_M_AF_LOCK 0x80 +#define MASK_M_AF_UNLOCK 0x40 +#define MASK_M_CBIT_FS 0x02 + +#define AUDIO_INTM 0x8516 +#define MASK_M_BUFINIT_END 0x01 + +#define ERR_INTM 0x8517 +#define MASK_M_EESS_ERR 0x80 + +#define HDCP_INTM 0x8518 +#define MASK_M_AVM_SET 0x80 +#define MASK_M_AVM_CLR 0x40 +#define MASK_M_LINKERR 0x20 +#define MASK_M_SHA_END 0x10 +#define MASK_M_R0_END 0x08 +#define MASK_M_KM_END 0x04 +#define MASK_M_AKSV_END 0x02 +#define MASK_M_AN_END 0x01 + +#define MISC_INTM 0x851B +#define MASK_M_AS_LAYOUT 0x10 +#define MASK_M_NO_SPD 0x08 +#define MASK_M_NO_VS 0x03 +#define MASK_M_SYNC_CHG 0x02 +#define MASK_M_AUDIO_MUTE 0x01 + +#define KEY_INTM 0x851F + +#define SYS_STATUS 0x8520 +#define MASK_S_SYNC 0x80 +#define MASK_S_AVMUTE 0x40 +#define MASK_S_HDCP 0x20 +#define MASK_S_HDMI 0x10 +#define MASK_S_PHY_SCDT 0x08 +#define MASK_S_PHY_PLL 0x04 +#define MASK_S_TMDS 0x02 +#define MASK_S_DDC5V 0x01 + +#define CSI_STATUS 0x0410 +#define MASK_S_WSYNC 0x0400 +#define MASK_S_TXACT 0x0200 +#define MASK_S_RXACT 0x0100 +#define MASK_S_HLT 0x0001 + +#define VI_STATUS1 0x8522 +#define MASK_S_V_GBD 0x08 +#define MASK_S_DEEPCOLOR 0x0c +#define MASK_S_V_422 0x02 +#define MASK_S_V_INTERLACE 0x01 + +#define AU_STATUS0 0x8523 +#define MASK_S_A_SAMPLE 0x01 + +#define VI_STATUS3 0x8528 +#define MASK_S_V_COLOR 0x1e +#define MASK_LIMITED 0x01 + +#define PHY_CTL0 0x8531 +#define MASK_PHY_SYSCLK_IND 0x02 +#define MASK_PHY_CTL 0x01 + + +#define PHY_CTL1 0x8532 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST1 0xf0 +#define MASK_PHY_AUTO_RST1_OFF 0x00 +#define SET_PHY_AUTO_RST1_US(us) ((((us) / 200) << 4) & \ + MASK_PHY_AUTO_RST1) +#define MASK_FREQ_RANGE_MODE 0x0f +#define SET_FREQ_RANGE_MODE_CYCLES(cycles) (((cycles) - 1) & \ + MASK_FREQ_RANGE_MODE) + +#define PHY_CTL2 0x8533 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST4 0x04 +#define MASK_PHY_AUTO_RST3 0x02 +#define MASK_PHY_AUTO_RST2 0x01 +#define MASK_PHY_AUTO_RSTn (MASK_PHY_AUTO_RST4 | \ + MASK_PHY_AUTO_RST3 | \ + MASK_PHY_AUTO_RST2) + +#define PHY_EN 0x8534 +#define MASK_ENABLE_PHY 0x01 + +#define PHY_RST 0x8535 +#define MASK_RESET_CTRL 0x01 /* Reset active low */ + +#define PHY_BIAS 0x8536 /* Not in REF_01 */ + +#define PHY_CSQ 0x853F /* Not in REF_01 */ +#define MASK_CSQ_CNT 0x0f +#define SET_CSQ_CNT_LEVEL(n) (n & MASK_CSQ_CNT) + +#define SYS_FREQ0 0x8540 +#define SYS_FREQ1 0x8541 + +#define SYS_CLK 0x8542 /* Not in REF_01 */ +#define MASK_CLK_DIFF 0x0C +#define MASK_CLK_DIV 0x03 + +#define DDC_CTL 0x8543 +#define MASK_DDC_ACK_POL 0x08 +#define MASK_DDC_ACTION 0x04 +#define MASK_DDC5V_MODE 0x03 +#define MASK_DDC5V_MODE_0MS 0x00 +#define MASK_DDC5V_MODE_50MS 0x01 +#define MASK_DDC5V_MODE_100MS 0x02 +#define MASK_DDC5V_MODE_200MS 0x03 + +#define HPD_CTL 0x8544 +#define MASK_HPD_CTL0 0x10 +#define MASK_HPD_OUT0 0x01 + +#define ANA_CTL 0x8545 +#define MASK_APPL_PCSX 0x30 +#define MASK_APPL_PCSX_HIZ 0x00 +#define MASK_APPL_PCSX_L_FIX 0x10 +#define MASK_APPL_PCSX_H_FIX 0x20 +#define MASK_APPL_PCSX_NORMAL 0x30 +#define MASK_ANALOG_ON 0x01 + +#define AVM_CTL 0x8546 + +#define INIT_END 0x854A +#define MASK_INIT_END 0x01 + +#define HDMI_DET 0x8552 /* Not in REF_01 */ +#define MASK_HDMI_DET_MOD1 0x80 +#define MASK_HDMI_DET_MOD0 0x40 +#define MASK_HDMI_DET_V 0x30 +#define MASK_HDMI_DET_V_SYNC 0x00 +#define MASK_HDMI_DET_V_ASYNC_25MS 0x10 +#define MASK_HDMI_DET_V_ASYNC_50MS 0x20 +#define MASK_HDMI_DET_V_ASYNC_100MS 0x30 +#define MASK_HDMI_DET_NUM 0x0f + +#define HDCP_MODE 0x8560 +#define MASK_MODE_RST_TN 0x20 +#define MASK_LINE_REKEY 0x10 +#define MASK_AUTO_CLR 0x04 + +#define HDCP_REG1 0x8563 /* Not in REF_01 */ +#define MASK_AUTH_UNAUTH_SEL 0x70 +#define MASK_AUTH_UNAUTH_SEL_12_FRAMES 0x70 +#define MASK_AUTH_UNAUTH_SEL_8_FRAMES 0x60 +#define MASK_AUTH_UNAUTH_SEL_4_FRAMES 0x50 +#define MASK_AUTH_UNAUTH_SEL_2_FRAMES 0x40 +#define MASK_AUTH_UNAUTH_SEL_64_FRAMES 0x30 +#define MASK_AUTH_UNAUTH_SEL_32_FRAMES 0x20 +#define MASK_AUTH_UNAUTH_SEL_16_FRAMES 0x10 +#define MASK_AUTH_UNAUTH_SEL_ONCE 0x00 +#define MASK_AUTH_UNAUTH 0x01 +#define MASK_AUTH_UNAUTH_AUTO 0x01 + +#define HDCP_REG2 0x8564 /* Not in REF_01 */ +#define MASK_AUTO_P3_RESET 0x0F +#define SET_AUTO_P3_RESET_FRAMES(n) (n & MASK_AUTO_P3_RESET) +#define MASK_AUTO_P3_RESET_OFF 0x00 + +#define VI_MODE 0x8570 +#define MASK_RGB_DVI 0x08 /* Not in REF_01 */ + +#define VOUT_SET2 0x8573 +#define MASK_SEL422 0x80 +#define MASK_VOUT_422FIL_100 0x40 +#define MASK_VOUTCOLORMODE 0x03 +#define MASK_VOUTCOLORMODE_THROUGH 0x00 +#define MASK_VOUTCOLORMODE_AUTO 0x01 +#define MASK_VOUTCOLORMODE_MANUAL 0x03 + +#define VOUT_SET3 0x8574 +#define MASK_VOUT_EXTCNT 0x08 + +#define VI_REP 0x8576 +#define MASK_VOUT_COLOR_SEL 0xe0 +#define MASK_VOUT_COLOR_RGB_FULL 0x00 +#define MASK_VOUT_COLOR_RGB_LIMITED 0x20 +#define MASK_VOUT_COLOR_601_YCBCR_FULL 0x40 +#define MASK_VOUT_COLOR_601_YCBCR_LIMITED 0x60 +#define MASK_VOUT_COLOR_709_YCBCR_FULL 0x80 +#define MASK_VOUT_COLOR_709_YCBCR_LIMITED 0xa0 +#define MASK_VOUT_COLOR_FULL_TO_LIMITED 0xc0 +#define MASK_VOUT_COLOR_LIMITED_TO_FULL 0xe0 +#define MASK_IN_REP_HEN 0x10 +#define MASK_IN_REP 0x0f + +#define VI_MUTE 0x857F +#define MASK_AUTO_MUTE 0xc0 +#define MASK_VI_MUTE 0x10 + +#define DE_WIDTH_H_LO 0x8582 /* Not in REF_01 */ +#define DE_WIDTH_H_HI 0x8583 /* Not in REF_01 */ +#define DE_WIDTH_V_LO 0x8588 /* Not in REF_01 */ +#define DE_WIDTH_V_HI 0x8589 /* Not in REF_01 */ +#define H_SIZE_LO 0x858A /* Not in REF_01 */ +#define H_SIZE_HI 0x858B /* Not in REF_01 */ +#define V_SIZE_LO 0x858C /* Not in REF_01 */ +#define V_SIZE_HI 0x858D /* Not in REF_01 */ +#define FV_CNT_LO 0x85A1 /* Not in REF_01 */ +#define FV_CNT_HI 0x85A2 /* Not in REF_01 */ + +#define FH_MIN0 0x85AA /* Not in REF_01 */ +#define FH_MIN1 0x85AB /* Not in REF_01 */ +#define FH_MAX0 0x85AC /* Not in REF_01 */ +#define FH_MAX1 0x85AD /* Not in REF_01 */ + +#define HV_RST 0x85AF /* Not in REF_01 */ +#define MASK_H_PI_RST 0x20 +#define MASK_V_PI_RST 0x10 + +#define EDID_MODE 0x85C7 +#define MASK_EDID_SPEED 0x40 +#define MASK_EDID_MODE 0x03 +#define MASK_EDID_MODE_DISABLE 0x00 +#define MASK_EDID_MODE_DDC2B 0x01 +#define MASK_EDID_MODE_E_DDC 0x02 + +#define EDID_LEN1 0x85CA +#define EDID_LEN2 0x85CB + +#define HDCP_REG3 0x85D1 /* Not in REF_01 */ +#define KEY_RD_CMD 0x01 + +#define FORCE_MUTE 0x8600 +#define MASK_FORCE_AMUTE 0x10 +#define MASK_FORCE_DMUTE 0x01 + +#define CMD_AUD 0x8601 +#define MASK_CMD_BUFINIT 0x04 +#define MASK_CMD_LOCKDET 0x02 +#define MASK_CMD_MUTE 0x01 + +#define AUTO_CMD0 0x8602 +#define MASK_AUTO_MUTE7 0x80 +#define MASK_AUTO_MUTE6 0x40 +#define MASK_AUTO_MUTE5 0x20 +#define MASK_AUTO_MUTE4 0x10 +#define MASK_AUTO_MUTE3 0x08 +#define MASK_AUTO_MUTE2 0x04 +#define MASK_AUTO_MUTE1 0x02 +#define MASK_AUTO_MUTE0 0x01 + +#define AUTO_CMD1 0x8603 +#define MASK_AUTO_MUTE10 0x04 +#define MASK_AUTO_MUTE9 0x02 +#define MASK_AUTO_MUTE8 0x01 + +#define AUTO_CMD2 0x8604 +#define MASK_AUTO_PLAY3 0x08 +#define MASK_AUTO_PLAY2 0x04 + +#define BUFINIT_START 0x8606 +#define SET_BUFINIT_START_MS(milliseconds) ((milliseconds) / 100) + +#define FS_MUTE 0x8607 +#define MASK_FS_ELSE_MUTE 0x80 +#define MASK_FS22_MUTE 0x40 +#define MASK_FS24_MUTE 0x20 +#define MASK_FS88_MUTE 0x10 +#define MASK_FS96_MUTE 0x08 +#define MASK_FS176_MUTE 0x04 +#define MASK_FS192_MUTE 0x02 +#define MASK_FS_NO_MUTE 0x01 + +#define FS_IMODE 0x8620 +#define MASK_NLPCM_HMODE 0x40 +#define MASK_NLPCM_SMODE 0x20 +#define MASK_NLPCM_IMODE 0x10 +#define MASK_FS_HMODE 0x08 +#define MASK_FS_AMODE 0x04 +#define MASK_FS_SMODE 0x02 +#define MASK_FS_IMODE 0x01 + +#define FS_SET 0x8621 +#define MASK_FS 0x0f + +#define LOCKDET_REF0 0x8630 +#define LOCKDET_REF1 0x8631 +#define LOCKDET_REF2 0x8632 + +#define ACR_MODE 0x8640 +#define MASK_ACR_LOAD 0x10 +#define MASK_N_MODE 0x04 +#define MASK_CTS_MODE 0x01 + +#define ACR_MDF0 0x8641 +#define MASK_ACR_L2MDF 0x70 +#define MASK_ACR_L2MDF_0_PPM 0x00 +#define MASK_ACR_L2MDF_61_PPM 0x10 +#define MASK_ACR_L2MDF_122_PPM 0x20 +#define MASK_ACR_L2MDF_244_PPM 0x30 +#define MASK_ACR_L2MDF_488_PPM 0x40 +#define MASK_ACR_L2MDF_976_PPM 0x50 +#define MASK_ACR_L2MDF_1976_PPM 0x60 +#define MASK_ACR_L2MDF_3906_PPM 0x70 +#define MASK_ACR_L1MDF 0x07 +#define MASK_ACR_L1MDF_0_PPM 0x00 +#define MASK_ACR_L1MDF_61_PPM 0x01 +#define MASK_ACR_L1MDF_122_PPM 0x02 +#define MASK_ACR_L1MDF_244_PPM 0x03 +#define MASK_ACR_L1MDF_488_PPM 0x04 +#define MASK_ACR_L1MDF_976_PPM 0x05 +#define MASK_ACR_L1MDF_1976_PPM 0x06 +#define MASK_ACR_L1MDF_3906_PPM 0x07 + +#define ACR_MDF1 0x8642 +#define MASK_ACR_L3MDF 0x07 +#define MASK_ACR_L3MDF_0_PPM 0x00 +#define MASK_ACR_L3MDF_61_PPM 0x01 +#define MASK_ACR_L3MDF_122_PPM 0x02 +#define MASK_ACR_L3MDF_244_PPM 0x03 +#define MASK_ACR_L3MDF_488_PPM 0x04 +#define MASK_ACR_L3MDF_976_PPM 0x05 +#define MASK_ACR_L3MDF_1976_PPM 0x06 +#define MASK_ACR_L3MDF_3906_PPM 0x07 + +#define SDO_MODE1 0x8652 +#define MASK_SDO_BIT_LENG 0x70 +#define MASK_SDO_FMT 0x03 +#define MASK_SDO_FMT_RIGHT 0x00 +#define MASK_SDO_FMT_LEFT 0x01 +#define MASK_SDO_FMT_I2S 0x02 + +#define DIV_MODE 0x8665 /* Not in REF_01 */ +#define MASK_DIV_DLY 0xf0 +#define SET_DIV_DLY_MS(milliseconds) ((((milliseconds) / 100) << 4) & \ + MASK_DIV_DLY) +#define MASK_DIV_MODE 0x01 + +#define NCO_F0_MOD 0x8670 +#define MASK_NCO_F0_MOD 0x03 +#define MASK_NCO_F0_MOD_42MHZ 0x00 +#define MASK_NCO_F0_MOD_27MHZ 0x01 + +#define PK_INT_MODE 0x8709 +#define MASK_ISRC2_INT_MODE 0x80 +#define MASK_ISRC_INT_MODE 0x40 +#define MASK_ACP_INT_MODE 0x20 +#define MASK_VS_INT_MODE 0x10 +#define MASK_SPD_INT_MODE 0x08 +#define MASK_MS_INT_MODE 0x04 +#define MASK_AUD_INT_MODE 0x02 +#define MASK_AVI_INT_MODE 0x01 + +#define NO_PKT_LIMIT 0x870B +#define MASK_NO_ACP_LIMIT 0xf0 +#define SET_NO_ACP_LIMIT_MS(milliseconds) ((((milliseconds) / 80) << 4) & \ + MASK_NO_ACP_LIMIT) +#define MASK_NO_AVI_LIMIT 0x0f +#define SET_NO_AVI_LIMIT_MS(milliseconds) (((milliseconds) / 80) & \ + MASK_NO_AVI_LIMIT) + +#define NO_PKT_CLR 0x870C +#define MASK_NO_VS_CLR 0x40 +#define MASK_NO_SPD_CLR 0x20 +#define MASK_NO_ACP_CLR 0x10 +#define MASK_NO_AVI_CLR1 0x02 +#define MASK_NO_AVI_CLR0 0x01 + +#define ERR_PK_LIMIT 0x870D +#define NO_PKT_LIMIT2 0x870E +#define PK_AVI_0HEAD 0x8710 +#define PK_AVI_1HEAD 0x8711 +#define PK_AVI_2HEAD 0x8712 +#define PK_AVI_0BYTE 0x8713 +#define PK_AVI_1BYTE 0x8714 +#define PK_AVI_2BYTE 0x8715 +#define PK_AVI_3BYTE 0x8716 +#define PK_AVI_4BYTE 0x8717 +#define PK_AVI_5BYTE 0x8718 +#define PK_AVI_6BYTE 0x8719 +#define PK_AVI_7BYTE 0x871A +#define PK_AVI_8BYTE 0x871B +#define PK_AVI_9BYTE 0x871C +#define PK_AVI_10BYTE 0x871D +#define PK_AVI_11BYTE 0x871E +#define PK_AVI_12BYTE 0x871F +#define PK_AVI_13BYTE 0x8720 +#define PK_AVI_14BYTE 0x8721 +#define PK_AVI_15BYTE 0x8722 +#define PK_AVI_16BYTE 0x8723 + +#define BKSV 0x8800 + +#define BCAPS 0x8840 +#define MASK_HDMI_RSVD 0x80 +#define MASK_REPEATER 0x40 +#define MASK_READY 0x20 +#define MASK_FASTI2C 0x10 +#define MASK_1_1_FEA 0x02 +#define MASK_FAST_REAU 0x01 + +#define BSTATUS1 0x8842 +#define MASK_MAX_EXCED 0x08 + +#define EDID_RAM 0x8C00 +#define NO_GDB_LIMIT 0x9007 + +#endif diff --git a/kernel/drivers/media/i2c/tda7432.c b/kernel/drivers/media/i2c/tda7432.c index cf93021a6..d87168ade 100644 --- a/kernel/drivers/media/i2c/tda7432.c +++ b/kernel/drivers/media/i2c/tda7432.c @@ -331,13 +331,6 @@ static const struct v4l2_ctrl_ops tda7432_ctrl_ops = { static const struct v4l2_subdev_core_ops tda7432_core_ops = { .log_status = tda7432_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_ops tda7432_ops = { @@ -416,7 +409,6 @@ MODULE_DEVICE_TABLE(i2c, tda7432_id); static struct i2c_driver tda7432_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda7432", }, .probe = tda7432_probe, diff --git a/kernel/drivers/media/i2c/tda9840.c b/kernel/drivers/media/i2c/tda9840.c index fbdff8b24..f31e65958 100644 --- a/kernel/drivers/media/i2c/tda9840.c +++ b/kernel/drivers/media/i2c/tda9840.c @@ -199,7 +199,6 @@ MODULE_DEVICE_TABLE(i2c, tda9840_id); static struct i2c_driver tda9840_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda9840", }, .probe = tda9840_probe, diff --git a/kernel/drivers/media/i2c/tea6415c.c b/kernel/drivers/media/i2c/tea6415c.c index bbe1a99fd..084bd75bb 100644 --- a/kernel/drivers/media/i2c/tea6415c.c +++ b/kernel/drivers/media/i2c/tea6415c.c @@ -162,7 +162,6 @@ MODULE_DEVICE_TABLE(i2c, tea6415c_id); static struct i2c_driver tea6415c_driver = { .driver = { - .owner = THIS_MODULE, .name = "tea6415c", }, .probe = tea6415c_probe, diff --git a/kernel/drivers/media/i2c/tea6420.c b/kernel/drivers/media/i2c/tea6420.c index 30a8d7577..b7f4e58f3 100644 --- a/kernel/drivers/media/i2c/tea6420.c +++ b/kernel/drivers/media/i2c/tea6420.c @@ -144,7 +144,6 @@ MODULE_DEVICE_TABLE(i2c, tea6420_id); static struct i2c_driver tea6420_driver = { .driver = { - .owner = THIS_MODULE, .name = "tea6420", }, .probe = tea6420_probe, diff --git a/kernel/drivers/media/i2c/ths7303.c b/kernel/drivers/media/i2c/ths7303.c index 9f7fdb6b6..bda3a6540 100644 --- a/kernel/drivers/media/i2c/ths7303.c +++ b/kernel/drivers/media/i2c/ths7303.c @@ -377,7 +377,6 @@ MODULE_DEVICE_TABLE(i2c, ths7303_id); static struct i2c_driver ths7303_driver = { .driver = { - .owner = THIS_MODULE, .name = "ths73x3", }, .probe = ths7303_probe, diff --git a/kernel/drivers/media/i2c/tlv320aic23b.c b/kernel/drivers/media/i2c/tlv320aic23b.c index ef87f7b09..0370dd89f 100644 --- a/kernel/drivers/media/i2c/tlv320aic23b.c +++ b/kernel/drivers/media/i2c/tlv320aic23b.c @@ -122,13 +122,6 @@ static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { .log_status = tlv320aic23b_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { diff --git a/kernel/drivers/media/i2c/tvaudio.c b/kernel/drivers/media/i2c/tvaudio.c index 070c152da..2a8114a67 100644 --- a/kernel/drivers/media/i2c/tvaudio.c +++ b/kernel/drivers/media/i2c/tvaudio.c @@ -272,7 +272,7 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) return -EINVAL; } - /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + /* FIXME: it seems that the shadow bytes are wrong below !*/ /* update our shadow register set; print bytes if (debug > 0) */ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", @@ -2051,7 +2051,6 @@ MODULE_DEVICE_TABLE(i2c, tvaudio_id); static struct i2c_driver tvaudio_driver = { .driver = { - .owner = THIS_MODULE, .name = "tvaudio", }, .probe = tvaudio_probe, diff --git a/kernel/drivers/media/i2c/tvp514x.c b/kernel/drivers/media/i2c/tvp514x.c index 1c6bc306e..a93985a9b 100644 --- a/kernel/drivers/media/i2c/tvp514x.c +++ b/kernel/drivers/media/i2c/tvp514x.c @@ -746,54 +746,6 @@ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) return err; } -/** - * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @index: index of pixelcode to retrieve - * @code: receives the pixelcode - * - * Enumerates supported mediabus formats - */ -static int -tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) -{ - if (index) - return -EINVAL; - - *code = MEDIA_BUS_FMT_YUYV10_2X10; - return 0; -} - -/** - * tvp514x_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to the mediabus format structure - * - * Negotiates the image capture size and mediabus format. - */ -static int -tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp514x_decoder *decoder = to_decoder(sd); - enum tvp514x_std current_std; - - if (f == NULL) - return -EINVAL; - - /* Calculate height and width based on current standard */ - current_std = decoder->current_std; - - f->code = MEDIA_BUS_FMT_YUYV8_2X8; - f->width = decoder->std_list[current_std].width; - f->height = decoder->std_list[current_std].height; - f->field = V4L2_FIELD_INTERLACED; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", - f->width, f->height); - return 0; -} - /** * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm * @sd: pointer to standard V4L2 sub-device structure @@ -962,6 +914,9 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, struct tvp514x_decoder *decoder = to_decoder(sd); __u32 which = format->which; + if (format->pad) + return -EINVAL; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { format->format = decoder->format; return 0; @@ -1002,24 +957,10 @@ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_core_ops tvp514x_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - static const struct v4l2_subdev_video_ops tvp514x_video_ops = { .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, .querystd = tvp514x_querystd, - .enum_mbus_fmt = tvp514x_enum_mbus_fmt, - .g_mbus_fmt = tvp514x_mbus_fmt, - .try_mbus_fmt = tvp514x_mbus_fmt, - .s_mbus_fmt = tvp514x_mbus_fmt, .g_parm = tvp514x_g_parm, .s_parm = tvp514x_s_parm, .s_stream = tvp514x_s_stream, @@ -1032,7 +973,6 @@ static const struct v4l2_subdev_pad_ops tvp514x_pad_ops = { }; static const struct v4l2_subdev_ops tvp514x_ops = { - .core = &tvp514x_core_ops, .video = &tvp514x_video_ops, .pad = &tvp514x_pad_ops, }; diff --git a/kernel/drivers/media/i2c/tvp5150.c b/kernel/drivers/media/i2c/tvp5150.c index 68cdab9c0..3c5fb2509 100644 --- a/kernel/drivers/media/i2c/tvp5150.c +++ b/kernel/drivers/media/i2c/tvp5150.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -817,24 +818,29 @@ static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) } } -static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) +static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + if (code->pad || code->index) return -EINVAL; - *code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_2X8; return 0; } -static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *f) +static int tvp5150_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *f; struct tvp5150 *decoder = to_tvp5150(sd); - if (f == NULL) + if (!format || format->pad) return -EINVAL; + f = &format->format; + tvp5150_reset(sd, 0); f->width = decoder->rect.width; @@ -1068,10 +1074,6 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_std = tvp5150_s_std, .s_routing = tvp5150_s_routing, - .enum_mbus_fmt = tvp5150_enum_mbus_fmt, - .s_mbus_fmt = tvp5150_mbus_fmt, - .try_mbus_fmt = tvp5150_mbus_fmt, - .g_mbus_fmt = tvp5150_mbus_fmt, .s_crop = tvp5150_s_crop, .g_crop = tvp5150_g_crop, .cropcap = tvp5150_cropcap, @@ -1084,11 +1086,18 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .s_raw_fmt = tvp5150_s_raw_fmt, }; +static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { + .enum_mbus_code = tvp5150_enum_mbus_code, + .set_fmt = tvp5150_fill_fmt, + .get_fmt = tvp5150_fill_fmt, +}; + static const struct v4l2_subdev_ops tvp5150_ops = { .core = &tvp5150_core_ops, .tuner = &tvp5150_tuner_ops, .video = &tvp5150_video_ops, .vbi = &tvp5150_vbi_ops, + .pad = &tvp5150_pad_ops, }; @@ -1164,8 +1173,7 @@ static int tvp5150_probe(struct i2c_client *c, sd->ctrl_handler = &core->hdl; if (core->hdl.error) { res = core->hdl.error; - v4l2_ctrl_handler_free(&core->hdl); - return res; + goto err; } v4l2_ctrl_handler_setup(&core->hdl); @@ -1178,9 +1186,17 @@ static int tvp5150_probe(struct i2c_client *c, core->rect.left = 0; core->rect.width = TVP5150_H_MAX; + res = v4l2_async_register_subdev(sd); + if (res < 0) + goto err; + if (debug > 1) tvp5150_log_status(sd); return 0; + +err: + v4l2_ctrl_handler_free(&core->hdl); + return res; } static int tvp5150_remove(struct i2c_client *c) @@ -1192,7 +1208,7 @@ static int tvp5150_remove(struct i2c_client *c) "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); - v4l2_device_unregister_subdev(sd); + v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); return 0; } @@ -1207,7 +1223,6 @@ MODULE_DEVICE_TABLE(i2c, tvp5150_id); static struct i2c_driver tvp5150_driver = { .driver = { - .owner = THIS_MODULE, .name = "tvp5150", }, .probe = tvp5150_probe, diff --git a/kernel/drivers/media/i2c/tvp7002.c b/kernel/drivers/media/i2c/tvp7002.c index 787cdfb08..f617d8b74 100644 --- a/kernel/drivers/media/i2c/tvp7002.c +++ b/kernel/drivers/media/i2c/tvp7002.c @@ -610,31 +610,6 @@ static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } -/* - * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt - * @sd: pointer to standard V4L2 sub-device structure - * @f: pointer to mediabus format structure - * - * Negotiate the image capture size and mediabus format. - * There is only one possible format, so this single function works for - * get, set and try. - */ -static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) -{ - struct tvp7002 *device = to_tvp7002(sd); - const struct v4l2_bt_timings *bt = &device->current_timings->timings.bt; - - f->width = bt->width; - f->height = bt->height; - f->code = MEDIA_BUS_FMT_YUYV10_1X20; - f->field = device->current_timings->scanmode; - f->colorspace = device->current_timings->color_space; - - v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", - f->width, f->height); - return 0; -} - /* * tvp7002_query_dv() - query DV timings * @sd: pointer to standard V4L2 sub-device structure @@ -746,25 +721,6 @@ static int tvp7002_s_register(struct v4l2_subdev *sd, } #endif -/* - * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats - * @sd: pointer to standard V4L2 sub-device structure - * @index: format index - * @code: pointer to mediabus format - * - * Enumerate supported mediabus formats. - */ - -static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) -{ - /* Check requested format index is within range */ - if (index) - return -EINVAL; - *code = MEDIA_BUS_FMT_YUYV10_1X20; - return 0; -} - /* * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream * @sd: pointer to standard V4L2 sub-device structure @@ -905,13 +861,6 @@ tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cf /* V4L2 core operation handlers */ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { .log_status = tvp7002_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp7002_g_register, .s_register = tvp7002_s_register, @@ -924,10 +873,6 @@ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { .s_dv_timings = tvp7002_s_dv_timings, .query_dv_timings = tvp7002_query_dv_timings, .s_stream = tvp7002_s_stream, - .g_mbus_fmt = tvp7002_mbus_fmt, - .try_mbus_fmt = tvp7002_mbus_fmt, - .s_mbus_fmt = tvp7002_mbus_fmt, - .enum_mbus_fmt = tvp7002_enum_mbus_fmt, }; /* media pad related operation handlers */ diff --git a/kernel/drivers/media/i2c/tw9903.c b/kernel/drivers/media/i2c/tw9903.c index 12c7d211a..bef79cf74 100644 --- a/kernel/drivers/media/i2c/tw9903.c +++ b/kernel/drivers/media/i2c/tw9903.c @@ -266,7 +266,6 @@ MODULE_DEVICE_TABLE(i2c, tw9903_id); static struct i2c_driver tw9903_driver = { .driver = { - .owner = THIS_MODULE, .name = "tw9903", }, .probe = tw9903_probe, diff --git a/kernel/drivers/media/i2c/tw9906.c b/kernel/drivers/media/i2c/tw9906.c index 2672d8926..316a3113e 100644 --- a/kernel/drivers/media/i2c/tw9906.c +++ b/kernel/drivers/media/i2c/tw9906.c @@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(i2c, tw9906_id); static struct i2c_driver tw9906_driver = { .driver = { - .owner = THIS_MODULE, .name = "tw9906", }, .probe = tw9906_probe, diff --git a/kernel/drivers/media/i2c/upd64031a.c b/kernel/drivers/media/i2c/upd64031a.c index d248e6a12..2c0f955ab 100644 --- a/kernel/drivers/media/i2c/upd64031a.c +++ b/kernel/drivers/media/i2c/upd64031a.c @@ -241,7 +241,6 @@ MODULE_DEVICE_TABLE(i2c, upd64031a_id); static struct i2c_driver upd64031a_driver = { .driver = { - .owner = THIS_MODULE, .name = "upd64031a", }, .probe = upd64031a_probe, diff --git a/kernel/drivers/media/i2c/upd64083.c b/kernel/drivers/media/i2c/upd64083.c index 3a152ce72..f2057a434 100644 --- a/kernel/drivers/media/i2c/upd64083.c +++ b/kernel/drivers/media/i2c/upd64083.c @@ -213,7 +213,6 @@ MODULE_DEVICE_TABLE(i2c, upd64083_id); static struct i2c_driver upd64083_driver = { .driver = { - .owner = THIS_MODULE, .name = "upd64083", }, .probe = upd64083_probe, diff --git a/kernel/drivers/media/i2c/vp27smpx.c b/kernel/drivers/media/i2c/vp27smpx.c index 819ab6d12..d6c23bdbc 100644 --- a/kernel/drivers/media/i2c/vp27smpx.c +++ b/kernel/drivers/media/i2c/vp27smpx.c @@ -194,7 +194,6 @@ MODULE_DEVICE_TABLE(i2c, vp27smpx_id); static struct i2c_driver vp27smpx_driver = { .driver = { - .owner = THIS_MODULE, .name = "vp27smpx", }, .probe = vp27smpx_probe, diff --git a/kernel/drivers/media/i2c/vpx3220.c b/kernel/drivers/media/i2c/vpx3220.c index 016e766e7..4b564f17f 100644 --- a/kernel/drivers/media/i2c/vpx3220.c +++ b/kernel/drivers/media/i2c/vpx3220.c @@ -450,13 +450,6 @@ static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .init = vpx3220_init, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_video_ops vpx3220_video_ops = { @@ -567,7 +560,6 @@ MODULE_DEVICE_TABLE(i2c, vpx3220_id); static struct i2c_driver vpx3220_driver = { .driver = { - .owner = THIS_MODULE, .name = "vpx3220", }, .probe = vpx3220_probe, diff --git a/kernel/drivers/media/i2c/vs6624.c b/kernel/drivers/media/i2c/vs6624.c index 00e7f0439..4c72a18c0 100644 --- a/kernel/drivers/media/i2c/vs6624.c +++ b/kernel/drivers/media/i2c/vs6624.c @@ -557,21 +557,28 @@ static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - u32 *code) +static int vs6624_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (index >= ARRAY_SIZE(vs6624_formats)) + if (code->pad || code->index >= ARRAY_SIZE(vs6624_formats)) return -EINVAL; - *code = vs6624_formats[index].mbus_code; + code->code = vs6624_formats[code->index].mbus_code; return 0; } -static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int vs6624_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; + struct vs6624 *sensor = to_vs6624(sd); int index; + if (format->pad) + return -EINVAL; + for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) if (vs6624_formats[index].mbus_code == fmt->code) break; @@ -590,18 +597,11 @@ static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, fmt->height = fmt->height & (~3); fmt->field = V4L2_FIELD_NONE; fmt->colorspace = vs6624_formats[index].colorspace; - return 0; -} - -static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct vs6624 *sensor = to_vs6624(sd); - int ret; - ret = vs6624_try_mbus_fmt(sd, fmt); - if (ret) - return ret; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *fmt; + return 0; + } /* set image format */ switch (fmt->code) { @@ -648,12 +648,16 @@ static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int vs6624_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct vs6624 *sensor = to_vs6624(sd); - *fmt = sensor->fmt; + if (format->pad) + return -EINVAL; + + format->format = sensor->fmt; return 0; } @@ -738,18 +742,21 @@ static const struct v4l2_subdev_core_ops vs6624_core_ops = { }; static const struct v4l2_subdev_video_ops vs6624_video_ops = { - .enum_mbus_fmt = vs6624_enum_mbus_fmt, - .try_mbus_fmt = vs6624_try_mbus_fmt, - .s_mbus_fmt = vs6624_s_mbus_fmt, - .g_mbus_fmt = vs6624_g_mbus_fmt, .s_parm = vs6624_s_parm, .g_parm = vs6624_g_parm, .s_stream = vs6624_s_stream, }; +static const struct v4l2_subdev_pad_ops vs6624_pad_ops = { + .enum_mbus_code = vs6624_enum_mbus_code, + .get_fmt = vs6624_get_fmt, + .set_fmt = vs6624_set_fmt, +}; + static const struct v4l2_subdev_ops vs6624_ops = { .core = &vs6624_core_ops, .video = &vs6624_video_ops, + .pad = &vs6624_pad_ops, }; static int vs6624_probe(struct i2c_client *client, diff --git a/kernel/drivers/media/i2c/wm8739.c b/kernel/drivers/media/i2c/wm8739.c index 3be73f6a4..f086e5e6e 100644 --- a/kernel/drivers/media/i2c/wm8739.c +++ b/kernel/drivers/media/i2c/wm8739.c @@ -176,13 +176,6 @@ static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { static const struct v4l2_subdev_core_ops wm8739_core_ops = { .log_status = wm8739_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { @@ -272,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, wm8739_id); static struct i2c_driver wm8739_driver = { .driver = { - .owner = THIS_MODULE, .name = "wm8739", }, .probe = wm8739_probe, diff --git a/kernel/drivers/media/i2c/wm8775.c b/kernel/drivers/media/i2c/wm8775.c index bee7946fa..d33d2cd6d 100644 --- a/kernel/drivers/media/i2c/wm8775.c +++ b/kernel/drivers/media/i2c/wm8775.c @@ -318,7 +318,6 @@ MODULE_DEVICE_TABLE(i2c, wm8775_id); static struct i2c_driver wm8775_driver = { .driver = { - .owner = THIS_MODULE, .name = "wm8775", }, .probe = wm8775_probe, diff --git a/kernel/drivers/media/media-entity.c b/kernel/drivers/media/media-entity.c index 4d8e01c7b..767fe55ba 100644 --- a/kernel/drivers/media/media-entity.c +++ b/kernel/drivers/media/media-entity.c @@ -235,8 +235,8 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { - DECLARE_BITMAP(active, entity->num_pads); - DECLARE_BITMAP(has_no_links, entity->num_pads); + DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); + DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); unsigned int i; entity->stream_count++; @@ -282,9 +282,9 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, if (ret < 0 && ret != -ENOIOCTLCMD) { dev_dbg(entity->parent->dev, "link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n", - entity->name, link->source->index, - link->sink->entity->name, - link->sink->index, ret); + link->source->entity->name, + link->source->index, + entity->name, link->sink->index, ret); goto error; } } diff --git a/kernel/drivers/media/pci/Kconfig b/kernel/drivers/media/pci/Kconfig index 218144a99..48a611bc3 100644 --- a/kernel/drivers/media/pci/Kconfig +++ b/kernel/drivers/media/pci/Kconfig @@ -11,16 +11,17 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" source "drivers/media/pci/meye/Kconfig" +source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" +source "drivers/media/pci/tw68/Kconfig" +source "drivers/media/pci/zoran/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT comment "Media capture/analog TV support" source "drivers/media/pci/ivtv/Kconfig" -source "drivers/media/pci/zoran/Kconfig" source "drivers/media/pci/saa7146/Kconfig" -source "drivers/media/pci/solo6x10/Kconfig" -source "drivers/media/pci/tw68/Kconfig" +source "drivers/media/pci/dt3155/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT @@ -32,6 +33,7 @@ source "drivers/media/pci/cx88/Kconfig" source "drivers/media/pci/bt8xx/Kconfig" source "drivers/media/pci/saa7134/Kconfig" source "drivers/media/pci/saa7164/Kconfig" +source "drivers/media/pci/cobalt/Kconfig" endif @@ -47,6 +49,7 @@ source "drivers/media/pci/mantis/Kconfig" source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" source "drivers/media/pci/smipcie/Kconfig" +source "drivers/media/pci/netup_unidvb/Kconfig" endif endif #MEDIA_PCI_SUPPORT diff --git a/kernel/drivers/media/pci/Makefile b/kernel/drivers/media/pci/Makefile index 0baf0d296..5f8aacb8b 100644 --- a/kernel/drivers/media/pci/Makefile +++ b/kernel/drivers/media/pci/Makefile @@ -12,7 +12,8 @@ obj-y += ttpci/ \ ngene/ \ ddbridge/ \ saa7146/ \ - smipcie/ + smipcie/ \ + netup_unidvb/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ @@ -24,6 +25,8 @@ obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_TW68) += tw68/ +obj-$(CONFIG_VIDEO_DT3155) += dt3155/ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ +obj-$(CONFIG_VIDEO_COBALT) += cobalt/ diff --git a/kernel/drivers/media/pci/bt8xx/btcx-risc.c b/kernel/drivers/media/pci/bt8xx/btcx-risc.c index 00f0880b6..57c7f58c3 100644 --- a/kernel/drivers/media/pci/bt8xx/btcx-risc.c +++ b/kernel/drivers/media/pci/bt8xx/btcx-risc.c @@ -160,7 +160,6 @@ btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int m void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) { - struct v4l2_clip swap; int i,j,n; if (nclips < 2) @@ -168,9 +167,7 @@ btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) for (i = nclips-2; i >= 0; i--) { for (n = 0, j = 0; j <= i; j++) { if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; + swap(clips[j], clips[j + 1]); n++; } } diff --git a/kernel/drivers/media/pci/bt8xx/bttv-audio-hook.c b/kernel/drivers/media/pci/bt8xx/bttv-audio-hook.c index 2364d1658..9f1f9169f 100644 --- a/kernel/drivers/media/pci/bt8xx/bttv-audio-hook.c +++ b/kernel/drivers/media/pci/bt8xx/bttv-audio-hook.c @@ -54,23 +54,33 @@ void winview_volume(struct bttv *btv, __u16 volume) void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { - unsigned int con = 0; + unsigned int con; - if (set) { - gpio_inout(0x300, 0x300); - if (t->audmode & V4L2_TUNER_MODE_LANG1) - con = 0x000; - if (t->audmode & V4L2_TUNER_MODE_LANG2) - con = 0x300; - if (t->audmode & V4L2_TUNER_MODE_STEREO) - con = 0x200; -/* if (t->audmode & V4L2_TUNER_MODE_MONO) - * con = 0x100; */ - gpio_bits(0x300, con); - } else { - t->audmode = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; + } + + gpio_inout(0x300, 0x300); + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1: + default: + con = 0x000; + break; + case V4L2_TUNER_MODE_LANG2: + con = 0x300; + break; + case V4L2_TUNER_MODE_STEREO: + con = 0x200; + break; } + gpio_bits(0x300, con); } void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set) @@ -82,47 +92,51 @@ void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set) val = gpio_read(); if (set) { - con = 0x000; - if (t->audmode & V4L2_TUNER_MODE_LANG2) { - if (t->audmode & V4L2_TUNER_MODE_LANG1) { - /* LANG1 + LANG2 */ - con = 0x100; - } - else { - /* LANG2 */ - con = 0x300; - } + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG2: + con = 0x300; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + con = 0x100; + break; + default: + con = 0x000; + break; } if (con != (val & 0x300)) { gpio_bits(0x300, con); if (bttv_gpio) - bttv_gpio_tracking(btv,"gvbctv5pci"); + bttv_gpio_tracking(btv, "gvbctv5pci"); } } else { switch (val & 0x70) { case 0x10: t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + t->audmode = V4L2_TUNER_MODE_LANG1_LANG2; break; case 0x30: t->rxsubchans = V4L2_TUNER_SUB_LANG2; + t->audmode = V4L2_TUNER_MODE_LANG1_LANG2; break; case 0x50: t->rxsubchans = V4L2_TUNER_SUB_LANG1; + t->audmode = V4L2_TUNER_MODE_LANG1_LANG2; break; case 0x60: t->rxsubchans = V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; break; case 0x70: t->rxsubchans = V4L2_TUNER_SUB_MONO; + t->audmode = V4L2_TUNER_MODE_MONO; break; default: t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + t->audmode = V4L2_TUNER_MODE_LANG1; } - t->audmode = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; } } @@ -142,23 +156,32 @@ void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set) void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { - int val = 0; + int val; - if (set) { - if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ - val = 0x02; - if (t->audmode & V4L2_TUNER_MODE_STEREO) - val = 0x01; - if (val) { - gpio_bits(0x03,val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"avermedia"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; + } + + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG2: /* SAP */ + val = 0x02; + break; + case V4L2_TUNER_MODE_STEREO: + val = 0x01; + break; + default: return; } + gpio_bits(0x03, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "avermedia"); } @@ -166,19 +189,31 @@ void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { int val = 0; - if (set) { - if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ - val = 0x01; - if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */ - val = 0x02; - btaor(val, ~0x03, BT848_GPIO_DATA); - if (bttv_gpio) - bttv_gpio_tracking(btv,"avermedia"); - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + return; } + + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG2: /* SAP */ + val = 0x01; + break; + case V4L2_TUNER_MODE_STEREO: + val = 0x02; + break; + default: + val = 0; + break; + } + btaor(val, ~0x03, BT848_GPIO_DATA); + if (bttv_gpio) + bttv_gpio_tracking(btv, "avermedia"); } /* Lifetec 9415 handling */ @@ -192,23 +227,32 @@ void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set) return; } - if (set) { - if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */ - val = 0x0080; - if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */ - val = 0x0880; - if ((t->audmode & V4L2_TUNER_MODE_LANG1) || - (t->audmode & V4L2_TUNER_MODE_MONO)) - val = 0; - gpio_bits(0x0880, val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"lt9415"); - } else { - /* autodetect doesn't work with this card :-( */ - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + return; } + + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG2: /* A2 SAP */ + val = 0x0080; + break; + case V4L2_TUNER_MODE_STEREO: /* A2 stereo */ + val = 0x0880; + break; + default: + val = 0; + break; + } + + gpio_bits(0x0880, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "lt9415"); } /* TDA9821 on TerraTV+ Bt848, Bt878 */ @@ -216,45 +260,69 @@ void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { unsigned int con = 0; - if (set) { - gpio_inout(0x180000,0x180000); - if (t->audmode & V4L2_TUNER_MODE_LANG2) - con = 0x080000; - if (t->audmode & V4L2_TUNER_MODE_STEREO) - con = 0x180000; - gpio_bits(0x180000, con); - if (bttv_gpio) - bttv_gpio_tracking(btv,"terratv"); - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; + } + + gpio_inout(0x180000, 0x180000); + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG2: + con = 0x080000; + break; + case V4L2_TUNER_MODE_STEREO: + con = 0x180000; + break; + default: + con = 0; + break; } + gpio_bits(0x180000, con); + if (bttv_gpio) + bttv_gpio_tracking(btv, "terratv"); } void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { - unsigned long val = 0; + unsigned long val; - if (set) { - /*btor (0xc32000, BT848_GPIO_OUT_EN);*/ - if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */ - val = 0x420000; - if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */ - val = 0x420000; - if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ - val = 0x410000; - if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */ - val = 0x020000; - if (val) { - gpio_bits(0x430000, val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"winfast2000"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; } + + /*btor (0xc32000, BT848_GPIO_OUT_EN);*/ + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + val = 0x420000; + break; + case V4L2_TUNER_MODE_LANG2: /* SAP */ + val = 0x410000; + break; + case V4L2_TUNER_MODE_STEREO: + val = 0x020000; + break; + default: + return; + } + + gpio_bits(0x430000, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "winfast2000"); } /* @@ -272,23 +340,33 @@ void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set) if (btv->radio_user) return; - if (set) { - if (t->audmode & V4L2_TUNER_MODE_MONO) { - val = 0x01; - } - if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2)) - || (t->audmode & V4L2_TUNER_MODE_STEREO)) { - val = 0x02; - } - if (val) { - gpio_bits(0x03,val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"pvbt878p9b"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; } + + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + val = 0x01; + break; + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_STEREO: + val = 0x02; + break; + default: + return; + } + + gpio_bits(0x03, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "pvbt878p9b"); } /* @@ -298,28 +376,37 @@ void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set) */ void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { - unsigned int val = 0xffff; + unsigned int val; if (btv->radio_user) return; - if (set) { - if (t->audmode & V4L2_TUNER_MODE_MONO) { - val = 0x0000; - } - if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2)) - || (t->audmode & V4L2_TUNER_MODE_STEREO)) { - val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */ - } - if (val != 0xffff) { - gpio_bits(0x1800, val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"fv2000s"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; } + + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + val = 0x0000; + break; + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + case V4L2_TUNER_MODE_STEREO: + val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */ + break; + default: + return; + } + gpio_bits(0x1800, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "fv2000s"); } /* @@ -328,26 +415,33 @@ void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set) */ void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { - unsigned long val = 0; + unsigned long val; - if (set) { - if (t->audmode & V4L2_TUNER_MODE_MONO) - val = 0x040000; - if (t->audmode & V4L2_TUNER_MODE_LANG1) - val = 0; - if (t->audmode & V4L2_TUNER_MODE_LANG2) - val = 0x100000; - if (t->audmode & V4L2_TUNER_MODE_STEREO) - val = 0; - if (val) { - gpio_bits(0x140000, val); - if (bttv_gpio) - bttv_gpio_tracking(btv,"windvr"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; + } + + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + val = 0x040000; + break; + case V4L2_TUNER_MODE_LANG2: + val = 0x100000; + break; + default: + return; } + + gpio_bits(0x140000, val); + if (bttv_gpio) + bttv_gpio_tracking(btv, "windvr"); } /* @@ -360,23 +454,36 @@ void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set) /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */ - if (set) { - /* btor(***, BT848_GPIO_OUT_EN); */ - if (t->audmode & V4L2_TUNER_MODE_LANG1) - con = 0x00000000; - if (t->audmode & V4L2_TUNER_MODE_LANG2) - con = 0x00180000; - if (t->audmode & V4L2_TUNER_MODE_STEREO) - con = 0x00000000; - if (t->audmode & V4L2_TUNER_MODE_MONO) - con = 0x00060000; - if (con != 0xffffff) { - gpio_bits(0x1e0000,con); - if (bttv_gpio) - bttv_gpio_tracking(btv, "adtvk503"); - } - } else { - t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + if (!set) { + /* Not much to do here */ + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + return; } + + /* btor(***, BT848_GPIO_OUT_EN); */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1: + con = 0x00000000; + break; + case V4L2_TUNER_MODE_LANG2: + con = 0x00180000; + break; + case V4L2_TUNER_MODE_STEREO: + con = 0x00000000; + break; + case V4L2_TUNER_MODE_MONO: + con = 0x00060000; + break; + default: + return; + } + + gpio_bits(0x1e0000, con); + if (bttv_gpio) + bttv_gpio_tracking(btv, "adtvk503"); } diff --git a/kernel/drivers/media/pci/bt8xx/bttv-driver.c b/kernel/drivers/media/pci/bt8xx/bttv-driver.c index bc12060e0..15a4ebc28 100644 --- a/kernel/drivers/media/pci/bt8xx/bttv-driver.c +++ b/kernel/drivers/media/pci/bt8xx/bttv-driver.c @@ -2676,7 +2676,8 @@ static int bttv_s_fbuf(struct file *file, void *f, fh->ov.w.height = fb->fmt.height; btv->init.ov.w.width = fb->fmt.width; btv->init.ov.w.height = fb->fmt.height; - kfree(fh->ov.clips); + + kfree(fh->ov.clips); fh->ov.clips = NULL; fh->ov.nclips = 0; @@ -3624,13 +3625,10 @@ static void bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, unsigned int state) { - struct timeval ts; - if (NULL == wakeup) return; - v4l2_get_timestamp(&ts); - wakeup->vb.ts = ts; + v4l2_get_timestamp(&wakeup->vb.ts); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; wake_up(&wakeup->vb.done); @@ -4238,6 +4236,7 @@ fail0: iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); + pci_disable_device(btv->c.pci); return result; } @@ -4281,6 +4280,7 @@ static void bttv_remove(struct pci_dev *pci_dev) iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); + pci_disable_device(btv->c.pci); v4l2_device_unregister(&btv->c.v4l2_dev); bttvs[btv->c.nr] = NULL; diff --git a/kernel/drivers/media/pci/bt8xx/bttv-input.c b/kernel/drivers/media/pci/bt8xx/bttv-input.c index 67c8d6b2c..a75c53da2 100644 --- a/kernel/drivers/media/pci/bt8xx/bttv-input.c +++ b/kernel/drivers/media/pci/bt8xx/bttv-input.c @@ -194,21 +194,18 @@ static u32 bttv_rc5_decode(unsigned int code) static void bttv_rc5_timer_end(unsigned long data) { struct bttv_ir *ir = (struct bttv_ir *)data; - struct timeval tv; + ktime_t tv; u32 gap, rc5, scancode; u8 toggle, command, system; /* get time */ - do_gettimeofday(&tv); + tv = ktime_get(); + gap = ktime_to_us(ktime_sub(tv, ir->base_time)); /* avoid overflow with gap >1s */ - if (tv.tv_sec - ir->base_time.tv_sec > 1) { + if (gap > USEC_PER_SEC) { gap = 200000; - } else { - gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + - tv.tv_usec - ir->base_time.tv_usec; } - /* signal we're ready to start a new code */ ir->active = false; @@ -249,7 +246,7 @@ static void bttv_rc5_timer_end(unsigned long data) static int bttv_rc5_irq(struct bttv *btv) { struct bttv_ir *ir = btv->remote; - struct timeval tv; + ktime_t tv; u32 gpio; u32 gap; unsigned long current_jiffies; @@ -259,14 +256,12 @@ static int bttv_rc5_irq(struct bttv *btv) /* get time of bit */ current_jiffies = jiffies; - do_gettimeofday(&tv); + tv = ktime_get(); + gap = ktime_to_us(ktime_sub(tv, ir->base_time)); /* avoid overflow with gap >1s */ - if (tv.tv_sec - ir->base_time.tv_sec > 1) { + if (gap > USEC_PER_SEC) { gap = 200000; - } else { - gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + - tv.tv_usec - ir->base_time.tv_usec; } dprintk("RC5 IRQ: gap %d us for %s\n", diff --git a/kernel/drivers/media/pci/bt8xx/bttvp.h b/kernel/drivers/media/pci/bt8xx/bttvp.h index a444cfb35..31bf79d3b 100644 --- a/kernel/drivers/media/pci/bt8xx/bttvp.h +++ b/kernel/drivers/media/pci/bt8xx/bttvp.h @@ -140,7 +140,7 @@ struct bttv_ir { bool rc5_gpio; /* Is RC5 legacy GPIO enabled? */ u32 last_bit; /* last raw bit seen */ u32 code; /* raw code under construction */ - struct timeval base_time; /* time of last seen code */ + ktime_t base_time; /* time of last seen code */ bool active; /* building raw code */ }; diff --git a/kernel/drivers/media/pci/bt8xx/dst.c b/kernel/drivers/media/pci/bt8xx/dst.c index f2261dfe5..4a90eee5e 100644 --- a/kernel/drivers/media/pci/bt8xx/dst.c +++ b/kernel/drivers/media/pci/bt8xx/dst.c @@ -425,7 +425,8 @@ static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth) return 0; } -static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion) +static int dst_set_inversion(struct dst_state *state, + enum fe_spectral_inversion inversion) { state->inversion = inversion; switch (inversion) { @@ -442,13 +443,13 @@ static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t in return 0; } -static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec) +static int dst_set_fec(struct dst_state *state, enum fe_code_rate fec) { state->fec = fec; return 0; } -static fe_code_rate_t dst_get_fec(struct dst_state *state) +static enum fe_code_rate dst_get_fec(struct dst_state *state) { return state->fec; } @@ -499,7 +500,8 @@ static int dst_set_symbolrate(struct dst_state *state, u32 srate) return 0; } -static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation) +static int dst_set_modulation(struct dst_state *state, + enum fe_modulation modulation) { if (state->dst_type != DST_TYPE_IS_CABLE) return -EOPNOTSUPP; @@ -536,7 +538,7 @@ static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulatio return 0; } -static fe_modulation_t dst_get_modulation(struct dst_state *state) +static enum fe_modulation dst_get_modulation(struct dst_state *state) { return state->modulation; } @@ -1376,7 +1378,8 @@ static int dst_get_tuna(struct dst_state *state) return 1; } -static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); +static int dst_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); static int dst_write_tuna(struct dvb_frontend *fe) { @@ -1466,7 +1469,7 @@ static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd return dst_command(state, paket, 8); } -static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int dst_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { int need_cmd, retval = 0; struct dst_state *state = fe->demodulator_priv; @@ -1500,7 +1503,7 @@ static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return retval; } -static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int dst_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct dst_state *state = fe->demodulator_priv; @@ -1525,7 +1528,7 @@ static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) return dst_tone_power_cmd(state); } -static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) +static int dst_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd minicmd) { struct dst_state *state = fe->demodulator_priv; @@ -1575,7 +1578,7 @@ static int bt8xx_dst_init(struct dvb_frontend *fe) return 0; } -static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int dst_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct dst_state *state = fe->demodulator_priv; @@ -1646,7 +1649,7 @@ static int dst_tune_frontend(struct dvb_frontend* fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { struct dst_state *state = fe->demodulator_priv; struct dtv_frontend_properties *p = &fe->dtv_property_cache; diff --git a/kernel/drivers/media/pci/bt8xx/dst_ca.c b/kernel/drivers/media/pci/bt8xx/dst_ca.c index c22c4ae06..c5cc14ef8 100644 --- a/kernel/drivers/media/pci/bt8xx/dst_ca.c +++ b/kernel/drivers/media/pci/bt8xx/dst_ca.c @@ -320,29 +320,27 @@ static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) return -EFAULT; - if (p_ca_message->msg) { - dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]", - 3, p_ca_message->msg); - - for (i = 0; i < 3; i++) { - command = command | p_ca_message->msg[i]; - if (i < 2) - command = command << 8; - } - dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); + dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]", + 3, p_ca_message->msg); - switch (command) { - case CA_APP_INFO: - memcpy(p_ca_message->msg, state->messages, 128); - if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) - return -EFAULT; - break; - case CA_INFO: - memcpy(p_ca_message->msg, state->messages, 128); - if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) - return -EFAULT; - break; - } + for (i = 0; i < 3; i++) { + command = command | p_ca_message->msg[i]; + if (i < 2) + command = command << 8; + } + dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); + + switch (command) { + case CA_APP_INFO: + memcpy(p_ca_message->msg, state->messages, 128); + if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) + return -EFAULT; + break; + case CA_INFO: + memcpy(p_ca_message->msg, state->messages, 128); + if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) + return -EFAULT; + break; } return 0; @@ -494,60 +492,58 @@ static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, goto free_mem_and_exit; } + /* EN50221 tag */ + command = 0; - if (p_ca_message->msg) { - /* EN50221 tag */ - command = 0; + for (i = 0; i < 3; i++) { + command = command | p_ca_message->msg[i]; + if (i < 2) + command = command << 8; + } + dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); - for (i = 0; i < 3; i++) { - command = command | p_ca_message->msg[i]; - if (i < 2) - command = command << 8; + switch (command) { + case CA_PMT: + dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); + if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); + result = -1; + goto free_mem_and_exit; } - dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); - - switch (command) { - case CA_PMT: - dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); - if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); - break; - case CA_PMT_REPLY: - dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); - /* Have to handle the 2 basic types of cards here */ - if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); - break; - case CA_APP_INFO_ENQUIRY: // only for debugging - dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); - - if ((ca_get_app_info(state)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); - break; - case CA_INFO_ENQUIRY: - dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); - - if ((ca_get_ca_info(state)) < 0) { - dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); - result = -1; - goto free_mem_and_exit; - } - dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); - break; + dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); + break; + case CA_PMT_REPLY: + dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); + /* Have to handle the 2 basic types of cards here */ + if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); + break; + case CA_APP_INFO_ENQUIRY: // only for debugging + dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); + + if ((ca_get_app_info(state)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); + result = -1; + goto free_mem_and_exit; } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); + break; + case CA_INFO_ENQUIRY: + dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); + + if ((ca_get_ca_info(state)) < 0) { + dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); + result = -1; + goto free_mem_and_exit; + } + dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); + break; } + free_mem_and_exit: kfree (hw_buffer); diff --git a/kernel/drivers/media/pci/bt8xx/dst_common.h b/kernel/drivers/media/pci/bt8xx/dst_common.h index d70d98f1a..6a2cfdd44 100644 --- a/kernel/drivers/media/pci/bt8xx/dst_common.h +++ b/kernel/drivers/media/pci/bt8xx/dst_common.h @@ -113,11 +113,11 @@ struct dst_state { u8 dst_type; u32 type_flags; u32 frequency; /* intermediate frequency in kHz for QPSK */ - fe_spectral_inversion_t inversion; + enum fe_spectral_inversion inversion; u32 symbol_rate; /* symbol rate in Symbols per second */ - fe_code_rate_t fec; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone; + enum fe_code_rate fec; + enum fe_sec_voltage voltage; + enum fe_sec_tone_mode tone; u32 decode_freq; u8 decode_lock; u16 decode_strength; @@ -127,8 +127,8 @@ struct dst_state { u32 bandwidth; u32 dst_hw_cap; u8 dst_fw_version; - fe_sec_mini_cmd_t minicmd; - fe_modulation_t modulation; + enum fe_sec_mini_cmd minicmd; + enum fe_modulation modulation; u8 messages[256]; u8 mac_address[8]; u8 fw_version[8]; diff --git a/kernel/drivers/media/pci/cobalt/Kconfig b/kernel/drivers/media/pci/cobalt/Kconfig new file mode 100644 index 000000000..a01f0cc74 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/Kconfig @@ -0,0 +1,20 @@ +config VIDEO_COBALT + tristate "Cisco Cobalt support" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on PCI_MSI && MTD_COMPLEX_MAPPINGS + depends on GPIOLIB || COMPILE_TEST + depends on SND + select I2C_ALGOBIT + select VIDEO_ADV7604 + select VIDEO_ADV7511 + select VIDEO_ADV7842 + select VIDEOBUF2_DMA_SG + ---help--- + This is a video4linux driver for the Cisco PCIe Cobalt card. + + This board is sadly not available outside of Cisco, but it is + very useful as an example of a real driver that uses all the + latest frameworks and APIs. + + To compile this driver as a module, choose M here: the + module will be called cobalt. diff --git a/kernel/drivers/media/pci/cobalt/Makefile b/kernel/drivers/media/pci/cobalt/Makefile new file mode 100644 index 000000000..b328955ab --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/Makefile @@ -0,0 +1,5 @@ +cobalt-objs := cobalt-driver.o cobalt-irq.o cobalt-v4l2.o \ + cobalt-i2c.o cobalt-omnitek.o cobalt-flash.o cobalt-cpld.o \ + cobalt-alsa-main.o cobalt-alsa-pcm.o + +obj-$(CONFIG_VIDEO_COBALT) += cobalt.o diff --git a/kernel/drivers/media/pci/cobalt/cobalt-alsa-main.c b/kernel/drivers/media/pci/cobalt/cobalt-alsa-main.c new file mode 100644 index 000000000..720e3ad93 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-alsa-main.c @@ -0,0 +1,162 @@ +/* + * ALSA interface to cobalt PCM capture streams + * + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "cobalt-driver.h" +#include "cobalt-alsa.h" +#include "cobalt-alsa-pcm.h" + +static void snd_cobalt_card_free(struct snd_cobalt_card *cobsc) +{ + if (cobsc == NULL) + return; + + cobsc->s->alsa = NULL; + + kfree(cobsc); +} + +static void snd_cobalt_card_private_free(struct snd_card *sc) +{ + if (sc == NULL) + return; + snd_cobalt_card_free(sc->private_data); + sc->private_data = NULL; + sc->private_free = NULL; +} + +static int snd_cobalt_card_create(struct cobalt_stream *s, + struct snd_card *sc, + struct snd_cobalt_card **cobsc) +{ + *cobsc = kzalloc(sizeof(struct snd_cobalt_card), GFP_KERNEL); + if (*cobsc == NULL) + return -ENOMEM; + + (*cobsc)->s = s; + (*cobsc)->sc = sc; + + sc->private_data = *cobsc; + sc->private_free = snd_cobalt_card_private_free; + + return 0; +} + +static int snd_cobalt_card_set_names(struct snd_cobalt_card *cobsc) +{ + struct cobalt_stream *s = cobsc->s; + struct cobalt *cobalt = s->cobalt; + struct snd_card *sc = cobsc->sc; + + /* sc->driver is used by alsa-lib's configurator: simple, unique */ + strlcpy(sc->driver, "cobalt", sizeof(sc->driver)); + + /* sc->shortname is a symlink in /proc/asound: COBALT-M -> cardN */ + snprintf(sc->shortname, sizeof(sc->shortname), "cobalt-%d-%d", + cobalt->instance, s->video_channel); + + /* sc->longname is read from /proc/asound/cards */ + snprintf(sc->longname, sizeof(sc->longname), + "Cobalt %d HDMI %d", + cobalt->instance, s->video_channel); + + return 0; +} + +int cobalt_alsa_init(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + struct snd_card *sc = NULL; + struct snd_cobalt_card *cobsc; + int ret; + + /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ + + /* (1) Check and increment the device index */ + /* This is a no-op for us. We'll use the cobalt->instance */ + + /* (2) Create a card instance */ + ret = snd_card_new(&cobalt->pci_dev->dev, SNDRV_DEFAULT_IDX1, + SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &sc); + if (ret) { + cobalt_err("snd_card_new() failed with err %d\n", ret); + goto err_exit; + } + + /* (3) Create a main component */ + ret = snd_cobalt_card_create(s, sc, &cobsc); + if (ret) { + cobalt_err("snd_cobalt_card_create() failed with err %d\n", + ret); + goto err_exit_free; + } + + /* (4) Set the driver ID and name strings */ + snd_cobalt_card_set_names(cobsc); + + ret = snd_cobalt_pcm_create(cobsc); + if (ret) { + cobalt_err("snd_cobalt_pcm_create() failed with err %d\n", + ret); + goto err_exit_free; + } + /* FIXME - proc files */ + + /* (7) Set the driver data and return 0 */ + /* We do this out of normal order for PCI drivers to avoid races */ + s->alsa = cobsc; + + /* (6) Register the card instance */ + ret = snd_card_register(sc); + if (ret) { + s->alsa = NULL; + cobalt_err("snd_card_register() failed with err %d\n", ret); + goto err_exit_free; + } + + return 0; + +err_exit_free: + if (sc != NULL) + snd_card_free(sc); + kfree(cobsc); +err_exit: + return ret; +} + +void cobalt_alsa_exit(struct cobalt_stream *s) +{ + struct snd_cobalt_card *cobsc = s->alsa; + + if (cobsc) + snd_card_free(cobsc->sc); + s->alsa = NULL; +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.c b/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.c new file mode 100644 index 000000000..f0bdf10cf --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.c @@ -0,0 +1,603 @@ +/* + * ALSA PCM device for the + * ALSA interface to cobalt PCM capture streams + * + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include "cobalt-driver.h" +#include "cobalt-alsa.h" +#include "cobalt-alsa-pcm.h" + +static unsigned int pcm_debug; +module_param(pcm_debug, int, 0644); +MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); + +#define dprintk(fmt, arg...) \ + do { \ + if (pcm_debug) \ + pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \ + } while (0) + +static struct snd_pcm_hardware snd_cobalt_hdmi_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + + .rates = SNDRV_PCM_RATE_48000, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ + .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ + .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ + .periods_min = 1, + .periods_max = 4, +}; + +static struct snd_pcm_hardware snd_cobalt_playback = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + + .rates = SNDRV_PCM_RATE_48000, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ + .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ + .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ + .periods_min = 1, + .periods_max = 4, +}; + +static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) +{ + static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; + unsigned idx = 0; + + while (len >= (is_s32 ? 4 : 2)) { + unsigned offset = map[idx] * 4; + u32 val = src[offset + 1] + (src[offset + 2] << 8) + + (src[offset + 3] << 16); + + if (is_s32) { + *dst++ = 0; + *dst++ = val & 0xff; + } + *dst++ = (val >> 8) & 0xff; + *dst++ = (val >> 16) & 0xff; + len -= is_s32 ? 4 : 2; + idx++; + } +} + +static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc, + u8 *pcm_data, + size_t skip, + size_t samples) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned long flags; + unsigned int oldptr; + unsigned int stride; + int length = samples; + int period_elapsed = 0; + bool is_s32; + + dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc, + pcm_data, samples); + + substream = cobsc->capture_pcm_substream; + if (substream == NULL) { + dprintk("substream was NULL\n"); + return; + } + + runtime = substream->runtime; + if (runtime == NULL) { + dprintk("runtime was NULL\n"); + return; + } + is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; + + stride = runtime->frame_bits >> 3; + if (stride == 0) { + dprintk("stride is zero\n"); + return; + } + + if (length == 0) { + dprintk("%s: length was zero\n", __func__); + return; + } + + if (runtime->dma_area == NULL) { + dprintk("dma area was NULL - ignoring\n"); + return; + } + + oldptr = cobsc->hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - oldptr; + unsigned i; + + for (i = 0; i < cnt; i++) + sample_cpy(runtime->dma_area + (oldptr + i) * stride, + pcm_data + i * skip, + stride, is_s32); + for (i = cnt; i < length; i++) + sample_cpy(runtime->dma_area + (i - cnt) * stride, + pcm_data + i * skip, stride, is_s32); + } else { + unsigned i; + + for (i = 0; i < length; i++) + sample_cpy(runtime->dma_area + (oldptr + i) * stride, + pcm_data + i * skip, + stride, is_s32); + } + snd_pcm_stream_lock_irqsave(substream, flags); + + cobsc->hwptr_done_capture += length; + if (cobsc->hwptr_done_capture >= + runtime->buffer_size) + cobsc->hwptr_done_capture -= + runtime->buffer_size; + + cobsc->capture_transfer_done += length; + if (cobsc->capture_transfer_done >= + runtime->period_size) { + cobsc->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock_irqrestore(substream, flags); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); +} + +static int alsa_fnc(struct vb2_buffer *vb, void *priv) +{ + struct cobalt_stream *s = priv; + unsigned char *p = vb2_plane_vaddr(vb, 0); + int i; + + if (pcm_debug) { + pr_info("alsa: "); + for (i = 0; i < 8 * 4; i++) { + if (!(i & 3)) + pr_cont(" "); + pr_cont("%02x", p[i]); + } + pr_cont("\n"); + } + cobalt_alsa_announce_pcm_data(s->alsa, + vb2_plane_vaddr(vb, 0), + 8 * 4, + vb2_get_plane_payload(vb, 0) / (8 * 4)); + return 0; +} + +static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + struct cobalt_stream *s = cobsc->s; + + runtime->hw = snd_cobalt_hdmi_capture; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + cobsc->capture_pcm_substream = substream; + runtime->private_data = s; + cobsc->alsa_record_cnt++; + if (cobsc->alsa_record_cnt == 1) { + int rc; + + rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name); + if (rc) { + cobsc->alsa_record_cnt--; + return rc; + } + } + return 0; +} + +static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + struct cobalt_stream *s = cobsc->s; + + cobsc->alsa_record_cnt--; + if (cobsc->alsa_record_cnt == 0) + vb2_thread_stop(&s->q); + return 0; +} + +static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + + +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +{ + struct snd_pcm_runtime *runtime = subs->runtime; + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + dprintk("%s called\n", __func__); + + return snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream *substream) +{ + if (substream->runtime->dma_area) { + dprintk("freeing pcm capture region\n"); + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + + cobsc->hwptr_done_capture = 0; + cobsc->capture_transfer_done = 0; + + return 0; +} + +static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + return 0; + default: + return -EINVAL; + } + return 0; +} + +static +snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t hwptr_done; + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + + hwptr_done = cobsc->hwptr_done_capture; + + return hwptr_done; +} + +static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) +{ + static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; + unsigned idx = 0; + + while (len >= (is_s32 ? 4 : 2)) { + unsigned offset = map[idx] * 4; + u8 *out = dst + offset; + + *out++ = 0; + if (is_s32) { + src++; + *out++ = *src++; + } else { + *out++ = 0; + } + *out++ = *src++; + *out = *src++; + len -= is_s32 ? 4 : 2; + idx++; + } +} + +static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc, + u8 *pcm_data, + size_t skip, + size_t samples) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned long flags; + unsigned int pos; + unsigned int stride; + bool is_s32; + unsigned i; + + dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc, + pcm_data, samples); + + substream = cobsc->playback_pcm_substream; + if (substream == NULL) { + dprintk("substream was NULL\n"); + return; + } + + runtime = substream->runtime; + if (runtime == NULL) { + dprintk("runtime was NULL\n"); + return; + } + + is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; + stride = runtime->frame_bits >> 3; + if (stride == 0) { + dprintk("stride is zero\n"); + return; + } + + if (samples == 0) { + dprintk("%s: samples was zero\n", __func__); + return; + } + + if (runtime->dma_area == NULL) { + dprintk("dma area was NULL - ignoring\n"); + return; + } + + pos = cobsc->pb_pos % cobsc->pb_size; + for (i = 0; i < cobsc->pb_count / (8 * 4); i++) + pb_sample_cpy(pcm_data + i * skip, + runtime->dma_area + pos + i * stride, + stride, is_s32); + snd_pcm_stream_lock_irqsave(substream, flags); + + cobsc->pb_pos += i * stride; + + snd_pcm_stream_unlock_irqrestore(substream, flags); + if (cobsc->pb_pos % cobsc->pb_count == 0) + snd_pcm_period_elapsed(substream); +} + +static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv) +{ + struct cobalt_stream *s = priv; + + if (s->alsa->alsa_pb_channel) + cobalt_alsa_pb_pcm_data(s->alsa, + vb2_plane_vaddr(vb, 0), + 8 * 4, + vb2_get_plane_payload(vb, 0) / (8 * 4)); + return 0; +} + +static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct cobalt_stream *s = cobsc->s; + + runtime->hw = snd_cobalt_playback; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + cobsc->playback_pcm_substream = substream; + runtime->private_data = s; + cobsc->alsa_playback_cnt++; + if (cobsc->alsa_playback_cnt == 1) { + int rc; + + rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name); + if (rc) { + cobsc->alsa_playback_cnt--; + return rc; + } + } + + return 0; +} + +static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + struct cobalt_stream *s = cobsc->s; + + cobsc->alsa_playback_cnt--; + if (cobsc->alsa_playback_cnt == 0) + vb2_thread_stop(&s->q); + return 0; +} + +static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + + cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream); + cobsc->pb_count = snd_pcm_lib_period_bytes(substream); + cobsc->pb_pos = 0; + + return 0; +} + +static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (cobsc->alsa_pb_channel) + return -EBUSY; + cobsc->alsa_pb_channel = true; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + cobsc->alsa_pb_channel = false; + return 0; + default: + return -EINVAL; + } +} + +static +snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream) +{ + struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = cobsc->pb_pos; + + return bytes_to_frames(substream->runtime, ptr) % + substream->runtime->buffer_size; +} + +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +static struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { + .open = snd_cobalt_pcm_capture_open, + .close = snd_cobalt_pcm_capture_close, + .ioctl = snd_cobalt_pcm_ioctl, + .hw_params = snd_cobalt_pcm_hw_params, + .hw_free = snd_cobalt_pcm_hw_free, + .prepare = snd_cobalt_pcm_prepare, + .trigger = snd_cobalt_pcm_trigger, + .pointer = snd_cobalt_pcm_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static struct snd_pcm_ops snd_cobalt_pcm_playback_ops = { + .open = snd_cobalt_pcm_playback_open, + .close = snd_cobalt_pcm_playback_close, + .ioctl = snd_cobalt_pcm_ioctl, + .hw_params = snd_cobalt_pcm_hw_params, + .hw_free = snd_cobalt_pcm_hw_free, + .prepare = snd_cobalt_pcm_pb_prepare, + .trigger = snd_cobalt_pcm_pb_trigger, + .pointer = snd_cobalt_pcm_pb_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc) +{ + struct snd_pcm *sp; + struct snd_card *sc = cobsc->sc; + struct cobalt_stream *s = cobsc->s; + struct cobalt *cobalt = s->cobalt; + int ret; + + s->q.gfp_flags |= __GFP_ZERO; + + if (!s->is_output) { + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), + 0); + mdelay(2); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), + 1); + mdelay(1); + + ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI", + 0, /* PCM device 0, the only one for this card */ + 0, /* 0 playback substreams */ + 1, /* 1 capture substream */ + &sp); + if (ret) { + cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n", + ret); + goto err_exit; + } + + snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, + &snd_cobalt_pcm_capture_ops); + sp->info_flags = 0; + sp->private_data = cobsc; + strlcpy(sp->name, "cobalt", sizeof(sp->name)); + } else { + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0); + mdelay(2); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1); + mdelay(1); + + ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI", + 0, /* PCM device 0, the only one for this card */ + 1, /* 0 playback substreams */ + 0, /* 1 capture substream */ + &sp); + if (ret) { + cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n", + ret); + goto err_exit; + } + + snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK, + &snd_cobalt_pcm_playback_ops); + sp->info_flags = 0; + sp->private_data = cobsc; + strlcpy(sp->name, "cobalt", sizeof(sp->name)); + } + + return 0; + +err_exit: + return ret; +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.h b/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.h new file mode 100644 index 000000000..513fb1f71 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-alsa-pcm.h @@ -0,0 +1,22 @@ +/* + * ALSA PCM device for the + * ALSA interface to cobalt PCM capture streams + * + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc); diff --git a/kernel/drivers/media/pci/cobalt/cobalt-alsa.h b/kernel/drivers/media/pci/cobalt/cobalt-alsa.h new file mode 100644 index 000000000..08db699ce --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-alsa.h @@ -0,0 +1,41 @@ +/* + * ALSA interface to cobalt PCM capture streams + * + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +struct snd_card; + +struct snd_cobalt_card { + struct cobalt_stream *s; + struct snd_card *sc; + unsigned int capture_transfer_done; + unsigned int hwptr_done_capture; + unsigned alsa_record_cnt; + struct snd_pcm_substream *capture_pcm_substream; + + unsigned int pb_size; + unsigned int pb_count; + unsigned int pb_pos; + unsigned pb_filled; + bool alsa_pb_channel; + unsigned alsa_playback_cnt; + struct snd_pcm_substream *playback_pcm_substream; +}; + +int cobalt_alsa_init(struct cobalt_stream *s); +void cobalt_alsa_exit(struct cobalt_stream *s); diff --git a/kernel/drivers/media/pci/cobalt/cobalt-cpld.c b/kernel/drivers/media/pci/cobalt/cobalt-cpld.c new file mode 100644 index 000000000..23c875fc1 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-cpld.c @@ -0,0 +1,339 @@ +/* + * Cobalt CPLD functions + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "cobalt-cpld.h" + +#define ADRS(offset) (COBALT_BUS_CPLD_BASE + offset) + +static u16 cpld_read(struct cobalt *cobalt, u32 offset) +{ + return cobalt_bus_read32(cobalt->bar1, ADRS(offset)); +} + +static void cpld_write(struct cobalt *cobalt, u32 offset, u16 val) +{ + return cobalt_bus_write32(cobalt->bar1, ADRS(offset), val); +} + +static void cpld_info_ver3(struct cobalt *cobalt) +{ + u32 rd; + u32 tmp; + + cobalt_info("CPLD System control register (read/write)\n"); + cobalt_info("\t\tSystem control: 0x%04x (0x0f00)\n", + cpld_read(cobalt, 0)); + cobalt_info("CPLD Clock control register (read/write)\n"); + cobalt_info("\t\tClock control: 0x%04x (0x0000)\n", + cpld_read(cobalt, 0x04)); + cobalt_info("CPLD HSMA Clk Osc register (read/write) - Must set wr trigger to load default values\n"); + cobalt_info("\t\tRegister #7:\t0x%04x (0x0022)\n", + cpld_read(cobalt, 0x08)); + cobalt_info("\t\tRegister #8:\t0x%04x (0x0047)\n", + cpld_read(cobalt, 0x0c)); + cobalt_info("\t\tRegister #9:\t0x%04x (0x00fa)\n", + cpld_read(cobalt, 0x10)); + cobalt_info("\t\tRegister #10:\t0x%04x (0x0061)\n", + cpld_read(cobalt, 0x14)); + cobalt_info("\t\tRegister #11:\t0x%04x (0x001e)\n", + cpld_read(cobalt, 0x18)); + cobalt_info("\t\tRegister #12:\t0x%04x (0x0045)\n", + cpld_read(cobalt, 0x1c)); + cobalt_info("\t\tRegister #135:\t0x%04x\n", + cpld_read(cobalt, 0x20)); + cobalt_info("\t\tRegister #137:\t0x%04x\n", + cpld_read(cobalt, 0x24)); + cobalt_info("CPLD System status register (read only)\n"); + cobalt_info("\t\tSystem status: 0x%04x\n", + cpld_read(cobalt, 0x28)); + cobalt_info("CPLD MAXII info register (read only)\n"); + cobalt_info("\t\tBoard serial number: 0x%04x\n", + cpld_read(cobalt, 0x2c)); + cobalt_info("\t\tMAXII program revision: 0x%04x\n", + cpld_read(cobalt, 0x30)); + cobalt_info("CPLD temp and voltage ADT7411 registers (read only)\n"); + cobalt_info("\t\tBoard temperature: %u Celcius\n", + cpld_read(cobalt, 0x34) / 4); + cobalt_info("\t\tFPGA temperature: %u Celcius\n", + cpld_read(cobalt, 0x38) / 4); + rd = cpld_read(cobalt, 0x3c); + tmp = (rd * 33 * 1000) / (483 * 10); + cobalt_info("\t\tVDD 3V3: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x40); + tmp = (rd * 74 * 2197) / (27 * 1000); + cobalt_info("\t\tADC ch3 5V: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x44); + tmp = (rd * 74 * 2197) / (47 * 1000); + cobalt_info("\t\tADC ch4 3V: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x48); + tmp = (rd * 57 * 2197) / (47 * 1000); + cobalt_info("\t\tADC ch5 2V5: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x4c); + tmp = (rd * 2197) / 1000; + cobalt_info("\t\tADC ch6 1V8: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x50); + tmp = (rd * 2197) / 1000; + cobalt_info("\t\tADC ch7 1V5: %u,%03uV\n", tmp / 1000, tmp % 1000); + rd = cpld_read(cobalt, 0x54); + tmp = (rd * 2197) / 1000; + cobalt_info("\t\tADC ch8 0V9: %u,%03uV\n", tmp / 1000, tmp % 1000); +} + +void cobalt_cpld_status(struct cobalt *cobalt) +{ + u32 rev = cpld_read(cobalt, 0x30); + + switch (rev) { + case 3: + case 4: + case 5: + cpld_info_ver3(cobalt); + break; + default: + cobalt_info("CPLD revision %u is not supported!\n", rev); + break; + } +} + +#define DCO_MIN 4850000000ULL +#define DCO_MAX 5670000000ULL + +#define SI570_CLOCK_CTRL 0x04 +#define S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_WR_TRIGGER 0x200 +#define S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_RST_TRIGGER 0x100 +#define S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_FPGA_CTRL 0x80 +#define S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN 0x40 + +#define SI570_REG7 0x08 +#define SI570_REG8 0x0c +#define SI570_REG9 0x10 +#define SI570_REG10 0x14 +#define SI570_REG11 0x18 +#define SI570_REG12 0x1c +#define SI570_REG135 0x20 +#define SI570_REG137 0x24 + +struct multiplier { + unsigned mult, hsdiv, n1; +}; + +/* List all possible multipliers (= hsdiv * n1). There are lots of duplicates, + which are all removed in this list to keep the list as short as possible. + The values for hsdiv and n1 are the actual values, not the register values. + */ +static const struct multiplier multipliers[] = { + { 4, 4, 1 }, { 5, 5, 1 }, { 6, 6, 1 }, + { 7, 7, 1 }, { 8, 4, 2 }, { 9, 9, 1 }, + { 10, 5, 2 }, { 11, 11, 1 }, { 12, 6, 2 }, + { 14, 7, 2 }, { 16, 4, 4 }, { 18, 9, 2 }, + { 20, 5, 4 }, { 22, 11, 2 }, { 24, 4, 6 }, + { 28, 7, 4 }, { 30, 5, 6 }, { 32, 4, 8 }, + { 36, 6, 6 }, { 40, 4, 10 }, { 42, 7, 6 }, + { 44, 11, 4 }, { 48, 4, 12 }, { 50, 5, 10 }, + { 54, 9, 6 }, { 56, 4, 14 }, { 60, 5, 12 }, + { 64, 4, 16 }, { 66, 11, 6 }, { 70, 5, 14 }, + { 72, 4, 18 }, { 80, 4, 20 }, { 84, 6, 14 }, + { 88, 11, 8 }, { 90, 5, 18 }, { 96, 4, 24 }, + { 98, 7, 14 }, { 100, 5, 20 }, { 104, 4, 26 }, + { 108, 6, 18 }, { 110, 11, 10 }, { 112, 4, 28 }, + { 120, 4, 30 }, { 126, 7, 18 }, { 128, 4, 32 }, + { 130, 5, 26 }, { 132, 11, 12 }, { 136, 4, 34 }, + { 140, 5, 28 }, { 144, 4, 36 }, { 150, 5, 30 }, + { 152, 4, 38 }, { 154, 11, 14 }, { 156, 6, 26 }, + { 160, 4, 40 }, { 162, 9, 18 }, { 168, 4, 42 }, + { 170, 5, 34 }, { 176, 11, 16 }, { 180, 5, 36 }, + { 182, 7, 26 }, { 184, 4, 46 }, { 190, 5, 38 }, + { 192, 4, 48 }, { 196, 7, 28 }, { 198, 11, 18 }, + { 198, 9, 22 }, { 200, 4, 50 }, { 204, 6, 34 }, + { 208, 4, 52 }, { 210, 5, 42 }, { 216, 4, 54 }, + { 220, 11, 20 }, { 224, 4, 56 }, { 228, 6, 38 }, + { 230, 5, 46 }, { 232, 4, 58 }, { 234, 9, 26 }, + { 238, 7, 34 }, { 240, 4, 60 }, { 242, 11, 22 }, + { 248, 4, 62 }, { 250, 5, 50 }, { 252, 6, 42 }, + { 256, 4, 64 }, { 260, 5, 52 }, { 264, 11, 24 }, + { 266, 7, 38 }, { 270, 5, 54 }, { 272, 4, 68 }, + { 276, 6, 46 }, { 280, 4, 70 }, { 286, 11, 26 }, + { 288, 4, 72 }, { 290, 5, 58 }, { 294, 7, 42 }, + { 296, 4, 74 }, { 300, 5, 60 }, { 304, 4, 76 }, + { 306, 9, 34 }, { 308, 11, 28 }, { 310, 5, 62 }, + { 312, 4, 78 }, { 320, 4, 80 }, { 322, 7, 46 }, + { 324, 6, 54 }, { 328, 4, 82 }, { 330, 11, 30 }, + { 336, 4, 84 }, { 340, 5, 68 }, { 342, 9, 38 }, + { 344, 4, 86 }, { 348, 6, 58 }, { 350, 5, 70 }, + { 352, 11, 32 }, { 360, 4, 90 }, { 364, 7, 52 }, + { 368, 4, 92 }, { 370, 5, 74 }, { 372, 6, 62 }, + { 374, 11, 34 }, { 376, 4, 94 }, { 378, 7, 54 }, + { 380, 5, 76 }, { 384, 4, 96 }, { 390, 5, 78 }, + { 392, 4, 98 }, { 396, 11, 36 }, { 400, 4, 100 }, + { 406, 7, 58 }, { 408, 4, 102 }, { 410, 5, 82 }, + { 414, 9, 46 }, { 416, 4, 104 }, { 418, 11, 38 }, + { 420, 5, 84 }, { 424, 4, 106 }, { 430, 5, 86 }, + { 432, 4, 108 }, { 434, 7, 62 }, { 440, 11, 40 }, + { 444, 6, 74 }, { 448, 4, 112 }, { 450, 5, 90 }, + { 456, 4, 114 }, { 460, 5, 92 }, { 462, 11, 42 }, + { 464, 4, 116 }, { 468, 6, 78 }, { 470, 5, 94 }, + { 472, 4, 118 }, { 476, 7, 68 }, { 480, 4, 120 }, + { 484, 11, 44 }, { 486, 9, 54 }, { 488, 4, 122 }, + { 490, 5, 98 }, { 492, 6, 82 }, { 496, 4, 124 }, + { 500, 5, 100 }, { 504, 4, 126 }, { 506, 11, 46 }, + { 510, 5, 102 }, { 512, 4, 128 }, { 516, 6, 86 }, + { 518, 7, 74 }, { 520, 5, 104 }, { 522, 9, 58 }, + { 528, 11, 48 }, { 530, 5, 106 }, { 532, 7, 76 }, + { 540, 5, 108 }, { 546, 7, 78 }, { 550, 11, 50 }, + { 552, 6, 92 }, { 558, 9, 62 }, { 560, 5, 112 }, + { 564, 6, 94 }, { 570, 5, 114 }, { 572, 11, 52 }, + { 574, 7, 82 }, { 576, 6, 96 }, { 580, 5, 116 }, + { 588, 6, 98 }, { 590, 5, 118 }, { 594, 11, 54 }, + { 600, 5, 120 }, { 602, 7, 86 }, { 610, 5, 122 }, + { 612, 6, 102 }, { 616, 11, 56 }, { 620, 5, 124 }, + { 624, 6, 104 }, { 630, 5, 126 }, { 636, 6, 106 }, + { 638, 11, 58 }, { 640, 5, 128 }, { 644, 7, 92 }, + { 648, 6, 108 }, { 658, 7, 94 }, { 660, 11, 60 }, + { 666, 9, 74 }, { 672, 6, 112 }, { 682, 11, 62 }, + { 684, 6, 114 }, { 686, 7, 98 }, { 696, 6, 116 }, + { 700, 7, 100 }, { 702, 9, 78 }, { 704, 11, 64 }, + { 708, 6, 118 }, { 714, 7, 102 }, { 720, 6, 120 }, + { 726, 11, 66 }, { 728, 7, 104 }, { 732, 6, 122 }, + { 738, 9, 82 }, { 742, 7, 106 }, { 744, 6, 124 }, + { 748, 11, 68 }, { 756, 6, 126 }, { 768, 6, 128 }, + { 770, 11, 70 }, { 774, 9, 86 }, { 784, 7, 112 }, + { 792, 11, 72 }, { 798, 7, 114 }, { 810, 9, 90 }, + { 812, 7, 116 }, { 814, 11, 74 }, { 826, 7, 118 }, + { 828, 9, 92 }, { 836, 11, 76 }, { 840, 7, 120 }, + { 846, 9, 94 }, { 854, 7, 122 }, { 858, 11, 78 }, + { 864, 9, 96 }, { 868, 7, 124 }, { 880, 11, 80 }, + { 882, 7, 126 }, { 896, 7, 128 }, { 900, 9, 100 }, + { 902, 11, 82 }, { 918, 9, 102 }, { 924, 11, 84 }, + { 936, 9, 104 }, { 946, 11, 86 }, { 954, 9, 106 }, + { 968, 11, 88 }, { 972, 9, 108 }, { 990, 11, 90 }, + { 1008, 9, 112 }, { 1012, 11, 92 }, { 1026, 9, 114 }, + { 1034, 11, 94 }, { 1044, 9, 116 }, { 1056, 11, 96 }, + { 1062, 9, 118 }, { 1078, 11, 98 }, { 1080, 9, 120 }, + { 1098, 9, 122 }, { 1100, 11, 100 }, { 1116, 9, 124 }, + { 1122, 11, 102 }, { 1134, 9, 126 }, { 1144, 11, 104 }, + { 1152, 9, 128 }, { 1166, 11, 106 }, { 1188, 11, 108 }, + { 1210, 11, 110 }, { 1232, 11, 112 }, { 1254, 11, 114 }, + { 1276, 11, 116 }, { 1298, 11, 118 }, { 1320, 11, 120 }, + { 1342, 11, 122 }, { 1364, 11, 124 }, { 1386, 11, 126 }, + { 1408, 11, 128 }, +}; + +bool cobalt_cpld_set_freq(struct cobalt *cobalt, unsigned f_out) +{ + const unsigned f_xtal = 39170000; /* xtal for si598 */ + u64 dco; + u64 rfreq; + unsigned delta = 0xffffffff; + unsigned i_best = 0; + unsigned i; + u8 n1, hsdiv; + u8 regs[6]; + int found = 0; + u16 clock_ctrl; + int retries = 3; + + for (i = 0; i < ARRAY_SIZE(multipliers); i++) { + unsigned mult = multipliers[i].mult; + u32 d; + + dco = (u64)f_out * mult; + if (dco < DCO_MIN || dco > DCO_MAX) + continue; + div_u64_rem((dco << 28) + f_xtal / 2, f_xtal, &d); + if (d < delta) { + found = 1; + i_best = i; + delta = d; + } + } + if (!found) + return false; + dco = (u64)f_out * multipliers[i_best].mult; + n1 = multipliers[i_best].n1 - 1; + hsdiv = multipliers[i_best].hsdiv - 4; + rfreq = div_u64(dco << 28, f_xtal); + + clock_ctrl = cpld_read(cobalt, SI570_CLOCK_CTRL); + clock_ctrl |= S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_FPGA_CTRL; + clock_ctrl |= S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN; + + regs[0] = (hsdiv << 5) | (n1 >> 2); + regs[1] = ((n1 & 0x3) << 6) | (rfreq >> 32); + regs[2] = (rfreq >> 24) & 0xff; + regs[3] = (rfreq >> 16) & 0xff; + regs[4] = (rfreq >> 8) & 0xff; + regs[5] = rfreq & 0xff; + + /* The sequence of clock_ctrl flags to set is very weird. It looks + like I have to reset it, then set the new frequency and reset it + again. It shouldn't be necessary to do a reset, but if I don't, + then a strange frequency is set (156.412034 MHz, or register values + 0x01, 0xc7, 0xfc, 0x7f, 0x53, 0x62). + */ + + cobalt_dbg(1, "%u: %6ph\n", f_out, regs); + + while (retries--) { + u8 read_regs[6]; + + cpld_write(cobalt, SI570_CLOCK_CTRL, + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN | + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_FPGA_CTRL); + usleep_range(10000, 15000); + cpld_write(cobalt, SI570_REG7, regs[0]); + cpld_write(cobalt, SI570_REG8, regs[1]); + cpld_write(cobalt, SI570_REG9, regs[2]); + cpld_write(cobalt, SI570_REG10, regs[3]); + cpld_write(cobalt, SI570_REG11, regs[4]); + cpld_write(cobalt, SI570_REG12, regs[5]); + cpld_write(cobalt, SI570_CLOCK_CTRL, + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN | + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_WR_TRIGGER); + usleep_range(10000, 15000); + cpld_write(cobalt, SI570_CLOCK_CTRL, + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN | + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_FPGA_CTRL); + usleep_range(10000, 15000); + read_regs[0] = cpld_read(cobalt, SI570_REG7); + read_regs[1] = cpld_read(cobalt, SI570_REG8); + read_regs[2] = cpld_read(cobalt, SI570_REG9); + read_regs[3] = cpld_read(cobalt, SI570_REG10); + read_regs[4] = cpld_read(cobalt, SI570_REG11); + read_regs[5] = cpld_read(cobalt, SI570_REG12); + cpld_write(cobalt, SI570_CLOCK_CTRL, + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN | + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_FPGA_CTRL | + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_RST_TRIGGER); + usleep_range(10000, 15000); + cpld_write(cobalt, SI570_CLOCK_CTRL, + S01755_REG_CLOCK_CTRL_BITMAP_CLKHSMA_EN); + usleep_range(10000, 15000); + + if (!memcmp(read_regs, regs, sizeof(read_regs))) + break; + cobalt_dbg(1, "retry: %6ph\n", read_regs); + } + if (2 - retries) + cobalt_info("Needed %d retries\n", 2 - retries); + + return true; +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-cpld.h b/kernel/drivers/media/pci/cobalt/cobalt-cpld.h new file mode 100644 index 000000000..0fc88fd5f --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-cpld.h @@ -0,0 +1,29 @@ +/* + * Cobalt CPLD functions + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COBALT_CPLD_H +#define COBALT_CPLD_H + +#include "cobalt-driver.h" + +void cobalt_cpld_status(struct cobalt *cobalt); +bool cobalt_cpld_set_freq(struct cobalt *cobalt, unsigned freq); + +#endif diff --git a/kernel/drivers/media/pci/cobalt/cobalt-driver.c b/kernel/drivers/media/pci/cobalt/cobalt-driver.c new file mode 100644 index 000000000..8fed61ec7 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-driver.c @@ -0,0 +1,833 @@ +/* + * cobalt driver initialization and card probing + * + * Derived from cx18-driver.c + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "cobalt-driver.h" +#include "cobalt-irq.h" +#include "cobalt-i2c.h" +#include "cobalt-v4l2.h" +#include "cobalt-flash.h" +#include "cobalt-alsa.h" +#include "cobalt-omnitek.h" + +/* add your revision and whatnot here */ +static struct pci_device_id cobalt_pci_tbl[] = { + {PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_COBALT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cobalt_pci_tbl); + +static atomic_t cobalt_instance = ATOMIC_INIT(0); + +int cobalt_debug; +module_param_named(debug, cobalt_debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level. Default: 0\n"); + +int cobalt_ignore_err; +module_param_named(ignore_err, cobalt_ignore_err, int, 0644); +MODULE_PARM_DESC(ignore_err, + "If set then ignore missing i2c adapters/receivers. Default: 0\n"); + +MODULE_AUTHOR("Hans Verkuil & Morten Hestnes"); +MODULE_DESCRIPTION("cobalt driver"); +MODULE_LICENSE("GPL"); + +static u8 edid[256] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x50, 0x21, 0x9C, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x12, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, + 0x0E, 0x00, 0xB2, 0xA0, 0x57, 0x49, 0x9B, 0x26, + 0x10, 0x48, 0x4F, 0x2F, 0xCF, 0x00, 0x31, 0x59, + 0x45, 0x59, 0x61, 0x59, 0x81, 0x99, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, + 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0xFD, 0x00, 0x31, 0x55, 0x18, + 0x5E, 0x11, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x43, + 0x20, 0x39, 0x30, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, + 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, + 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, + 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, + 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, + 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, + 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 +}; + +static void cobalt_set_interrupt(struct cobalt *cobalt, bool enable) +{ + if (enable) { + unsigned irqs = COBALT_SYSSTAT_VI0_INT1_MSK | + COBALT_SYSSTAT_VI1_INT1_MSK | + COBALT_SYSSTAT_VI2_INT1_MSK | + COBALT_SYSSTAT_VI3_INT1_MSK | + COBALT_SYSSTAT_VI0_INT2_MSK | + COBALT_SYSSTAT_VI1_INT2_MSK | + COBALT_SYSSTAT_VI2_INT2_MSK | + COBALT_SYSSTAT_VI3_INT2_MSK | + COBALT_SYSSTAT_VI0_LOST_DATA_MSK | + COBALT_SYSSTAT_VI1_LOST_DATA_MSK | + COBALT_SYSSTAT_VI2_LOST_DATA_MSK | + COBALT_SYSSTAT_VI3_LOST_DATA_MSK | + COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK; + + if (cobalt->have_hsma_rx) + irqs |= COBALT_SYSSTAT_VIHSMA_INT1_MSK | + COBALT_SYSSTAT_VIHSMA_INT2_MSK | + COBALT_SYSSTAT_VIHSMA_LOST_DATA_MSK; + + if (cobalt->have_hsma_tx) + irqs |= COBALT_SYSSTAT_VOHSMA_INT1_MSK | + COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK | + COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK; + /* Clear any existing interrupts */ + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_EDGE, 0xffffffff); + /* PIO Core interrupt mask register. + Enable ADV7604 INT1 interrupts */ + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, irqs); + } else { + /* Disable all ADV7604 interrupts */ + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, 0); + } +} + +static unsigned cobalt_get_sd_nr(struct v4l2_subdev *sd) +{ + struct cobalt *cobalt = to_cobalt(sd->v4l2_dev); + unsigned i; + + for (i = 0; i < COBALT_NUM_NODES; i++) + if (sd == cobalt->streams[i].sd) + return i; + cobalt_err("Invalid adv7604 subdev pointer!\n"); + return 0; +} + +static void cobalt_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct cobalt *cobalt = to_cobalt(sd->v4l2_dev); + unsigned sd_nr = cobalt_get_sd_nr(sd); + struct cobalt_stream *s = &cobalt->streams[sd_nr]; + bool hotplug = arg ? *((int *)arg) : false; + + if (s->is_output) + return; + + switch (notification) { + case ADV76XX_HOTPLUG: + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_HPD_TO_CONNECTOR_BIT(sd_nr), hotplug); + cobalt_dbg(1, "Set hotplug for adv %d to %d\n", sd_nr, hotplug); + break; + case V4L2_DEVICE_NOTIFY_EVENT: + cobalt_dbg(1, "Format changed for adv %d\n", sd_nr); + v4l2_event_queue(&s->vdev, arg); + break; + default: + break; + } +} + +static int get_payload_size(u16 code) +{ + switch (code) { + case 0: return 128; + case 1: return 256; + case 2: return 512; + case 3: return 1024; + case 4: return 2048; + case 5: return 4096; + default: return 0; + } + return 0; +} + +static const char *get_link_speed(u16 stat) +{ + switch (stat & PCI_EXP_LNKSTA_CLS) { + case 1: return "2.5 Gbit/s"; + case 2: return "5 Gbit/s"; + case 3: return "10 Gbit/s"; + } + return "Unknown speed"; +} + +void cobalt_pcie_status_show(struct cobalt *cobalt) +{ + struct pci_dev *pci_dev = cobalt->pci_dev; + struct pci_dev *pci_bus_dev = cobalt->pci_dev->bus->self; + int offset; + int bus_offset; + u32 capa; + u16 stat, ctrl; + + offset = pci_find_capability(pci_dev, PCI_CAP_ID_EXP); + bus_offset = pci_find_capability(pci_bus_dev, PCI_CAP_ID_EXP); + + /* Device */ + pci_read_config_dword(pci_dev, offset + PCI_EXP_DEVCAP, &capa); + pci_read_config_word(pci_dev, offset + PCI_EXP_DEVCTL, &ctrl); + pci_read_config_word(pci_dev, offset + PCI_EXP_DEVSTA, &stat); + cobalt_info("PCIe device capability 0x%08x: Max payload %d\n", + capa, get_payload_size(capa & PCI_EXP_DEVCAP_PAYLOAD)); + cobalt_info("PCIe device control 0x%04x: Max payload %d. Max read request %d\n", + ctrl, + get_payload_size((ctrl & PCI_EXP_DEVCTL_PAYLOAD) >> 5), + get_payload_size((ctrl & PCI_EXP_DEVCTL_READRQ) >> 12)); + cobalt_info("PCIe device status 0x%04x\n", stat); + + /* Link */ + pci_read_config_dword(pci_dev, offset + PCI_EXP_LNKCAP, &capa); + pci_read_config_word(pci_dev, offset + PCI_EXP_LNKCTL, &ctrl); + pci_read_config_word(pci_dev, offset + PCI_EXP_LNKSTA, &stat); + cobalt_info("PCIe link capability 0x%08x: %s per lane and %u lanes\n", + capa, get_link_speed(capa), + (capa & PCI_EXP_LNKCAP_MLW) >> 4); + cobalt_info("PCIe link control 0x%04x\n", ctrl); + cobalt_info("PCIe link status 0x%04x: %s per lane and %u lanes\n", + stat, get_link_speed(stat), + (stat & PCI_EXP_LNKSTA_NLW) >> 4); + + /* Bus */ + pci_read_config_dword(pci_bus_dev, bus_offset + PCI_EXP_LNKCAP, &capa); + cobalt_info("PCIe bus link capability 0x%08x: %s per lane and %u lanes\n", + capa, get_link_speed(capa), + (capa & PCI_EXP_LNKCAP_MLW) >> 4); + + /* Slot */ + pci_read_config_dword(pci_dev, offset + PCI_EXP_SLTCAP, &capa); + pci_read_config_word(pci_dev, offset + PCI_EXP_SLTCTL, &ctrl); + pci_read_config_word(pci_dev, offset + PCI_EXP_SLTSTA, &stat); + cobalt_info("PCIe slot capability 0x%08x\n", capa); + cobalt_info("PCIe slot control 0x%04x\n", ctrl); + cobalt_info("PCIe slot status 0x%04x\n", stat); +} + +static unsigned pcie_link_get_lanes(struct cobalt *cobalt) +{ + struct pci_dev *pci_dev = cobalt->pci_dev; + unsigned offset; + u16 link; + + offset = pci_find_capability(pci_dev, PCI_CAP_ID_EXP); + if (!offset) + return 0; + pci_read_config_word(pci_dev, offset + PCI_EXP_LNKSTA, &link); + return (link & PCI_EXP_LNKSTA_NLW) >> 4; +} + +static unsigned pcie_bus_link_get_lanes(struct cobalt *cobalt) +{ + struct pci_dev *pci_dev = cobalt->pci_dev->bus->self; + unsigned offset; + u32 link; + + offset = pci_find_capability(pci_dev, PCI_CAP_ID_EXP); + if (!offset) + return 0; + pci_read_config_dword(pci_dev, offset + PCI_EXP_LNKCAP, &link); + return (link & PCI_EXP_LNKCAP_MLW) >> 4; +} + +static void msi_config_show(struct cobalt *cobalt, struct pci_dev *pci_dev) +{ + u16 ctrl, data; + u32 adrs_l, adrs_h; + + pci_read_config_word(pci_dev, 0x52, &ctrl); + cobalt_info("MSI %s\n", ctrl & 1 ? "enable" : "disable"); + cobalt_info("MSI multiple message: Capable %u. Enable %u\n", + (1 << ((ctrl >> 1) & 7)), (1 << ((ctrl >> 4) & 7))); + if (ctrl & 0x80) + cobalt_info("MSI: 64-bit address capable\n"); + pci_read_config_dword(pci_dev, 0x54, &adrs_l); + pci_read_config_dword(pci_dev, 0x58, &adrs_h); + pci_read_config_word(pci_dev, 0x5c, &data); + if (ctrl & 0x80) + cobalt_info("MSI: Address 0x%08x%08x. Data 0x%04x\n", + adrs_h, adrs_l, data); + else + cobalt_info("MSI: Address 0x%08x. Data 0x%04x\n", + adrs_l, data); +} + +static void cobalt_pci_iounmap(struct cobalt *cobalt, struct pci_dev *pci_dev) +{ + if (cobalt->bar0) { + pci_iounmap(pci_dev, cobalt->bar0); + cobalt->bar0 = NULL; + } + if (cobalt->bar1) { + pci_iounmap(pci_dev, cobalt->bar1); + cobalt->bar1 = NULL; + } +} + +static void cobalt_free_msi(struct cobalt *cobalt, struct pci_dev *pci_dev) +{ + free_irq(pci_dev->irq, (void *)cobalt); + + if (cobalt->msi_enabled) + pci_disable_msi(pci_dev); +} + +static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + u32 ctrl; + int ret; + + cobalt_dbg(1, "enabling pci device\n"); + + ret = pci_enable_device(pci_dev); + if (ret) { + cobalt_err("can't enable device\n"); + return ret; + } + pci_set_master(pci_dev); + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cobalt->card_rev); + pci_read_config_word(pci_dev, PCI_DEVICE_ID, &cobalt->device_id); + + switch (cobalt->device_id) { + case PCI_DEVICE_ID_COBALT: + cobalt_info("PCI Express interface from Omnitek\n"); + break; + default: + cobalt_info("PCI Express interface provider is unknown!\n"); + break; + } + + if (pcie_link_get_lanes(cobalt) != 8) { + cobalt_warn("PCI Express link width is %d lanes.\n", + pcie_link_get_lanes(cobalt)); + if (pcie_bus_link_get_lanes(cobalt) < 8) + cobalt_warn("The current slot only supports %d lanes, for best performance 8 are needed\n", + pcie_bus_link_get_lanes(cobalt)); + if (pcie_link_get_lanes(cobalt) != pcie_bus_link_get_lanes(cobalt)) { + cobalt_err("The card is most likely not seated correctly in the PCIe slot\n"); + ret = -EIO; + goto err_disable; + } + } + + if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) { + ret = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (ret) { + cobalt_err("no suitable DMA available\n"); + goto err_disable; + } + } + + ret = pci_request_regions(pci_dev, "cobalt"); + if (ret) { + cobalt_err("error requesting regions\n"); + goto err_disable; + } + + cobalt_pcie_status_show(cobalt); + + cobalt->bar0 = pci_iomap(pci_dev, 0, 0); + cobalt->bar1 = pci_iomap(pci_dev, 1, 0); + if (cobalt->bar1 == NULL) { + cobalt->bar1 = pci_iomap(pci_dev, 2, 0); + cobalt_info("64-bit BAR\n"); + } + if (!cobalt->bar0 || !cobalt->bar1) { + ret = -EIO; + goto err_release; + } + + /* Reset the video inputs before enabling any interrupts */ + ctrl = cobalt_read_bar1(cobalt, COBALT_SYS_CTRL_BASE); + cobalt_write_bar1(cobalt, COBALT_SYS_CTRL_BASE, ctrl & ~0xf00); + + /* Disable interrupts to prevent any spurious interrupts + from being generated. */ + cobalt_set_interrupt(cobalt, false); + + if (pci_enable_msi_range(pci_dev, 1, 1) < 1) { + cobalt_err("Could not enable MSI\n"); + cobalt->msi_enabled = false; + ret = -EIO; + goto err_release; + } + msi_config_show(cobalt, pci_dev); + cobalt->msi_enabled = true; + + /* Register IRQ */ + if (request_irq(pci_dev->irq, cobalt_irq_handler, IRQF_SHARED, + cobalt->v4l2_dev.name, (void *)cobalt)) { + cobalt_err("Failed to register irq %d\n", pci_dev->irq); + ret = -EIO; + goto err_msi; + } + + omni_sg_dma_init(cobalt); + return 0; + +err_msi: + pci_disable_msi(pci_dev); + +err_release: + cobalt_pci_iounmap(cobalt, pci_dev); + pci_release_regions(pci_dev); + +err_disable: + pci_disable_device(cobalt->pci_dev); + return ret; +} + +static int cobalt_hdl_info_get(struct cobalt *cobalt) +{ + int i; + + for (i = 0; i < COBALT_HDL_INFO_SIZE; i++) + cobalt->hdl_info[i] = + ioread8(cobalt->bar1 + COBALT_HDL_INFO_BASE + i); + cobalt->hdl_info[COBALT_HDL_INFO_SIZE - 1] = '\0'; + if (strstr(cobalt->hdl_info, COBALT_HDL_SEARCH_STR)) + return 0; + + return 1; +} + +static void cobalt_stream_struct_init(struct cobalt *cobalt) +{ + int i; + + for (i = 0; i < COBALT_NUM_STREAMS; i++) { + struct cobalt_stream *s = &cobalt->streams[i]; + + s->cobalt = cobalt; + s->flags = 0; + s->is_audio = false; + s->is_output = false; + s->is_dummy = true; + + /* The Memory DMA channels will always get a lower channel + * number than the FIFO DMA. Video input should map to the + * stream 0-3. The other can use stream struct from 4 and + * higher */ + if (i <= COBALT_HSMA_IN_NODE) { + s->dma_channel = i + cobalt->first_fifo_channel; + s->video_channel = i; + s->dma_fifo_mask = + COBALT_SYSSTAT_VI0_LOST_DATA_MSK << (4 * i); + s->adv_irq_mask = + COBALT_SYSSTAT_VI0_INT1_MSK << (4 * i); + } else if (i >= COBALT_AUDIO_IN_STREAM && + i <= COBALT_AUDIO_IN_STREAM + 4) { + unsigned idx = i - COBALT_AUDIO_IN_STREAM; + + s->dma_channel = 6 + idx; + s->is_audio = true; + s->video_channel = idx; + s->dma_fifo_mask = COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK; + } else if (i == COBALT_HSMA_OUT_NODE) { + s->dma_channel = 11; + s->is_output = true; + s->video_channel = 5; + s->dma_fifo_mask = COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK; + s->adv_irq_mask = COBALT_SYSSTAT_VOHSMA_INT1_MSK; + } else if (i == COBALT_AUDIO_OUT_STREAM) { + s->dma_channel = 12; + s->is_audio = true; + s->is_output = true; + s->video_channel = 5; + s->dma_fifo_mask = COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK; + } else { + /* FIXME: Memory DMA for debug purpose */ + s->dma_channel = i - COBALT_NUM_NODES; + } + cobalt_info("stream #%d -> dma channel #%d <- video channel %d\n", + i, s->dma_channel, s->video_channel); + } +} + +static int cobalt_subdevs_init(struct cobalt *cobalt) +{ + static struct adv76xx_platform_data adv7604_pdata = { + .disable_pwrdnb = 1, + .ain_sel = ADV7604_AIN7_8_9_NC_SYNC_3_1, + .bus_order = ADV7604_BUS_ORDER_BRG, + .blank_data = 1, + .op_656_range = 1, + .op_format_mode_sel = ADV7604_OP_FORMAT_MODE0, + .int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH, + .dr_str_data = ADV76XX_DR_STR_HIGH, + .dr_str_clk = ADV76XX_DR_STR_HIGH, + .dr_str_sync = ADV76XX_DR_STR_HIGH, + .hdmi_free_run_mode = 1, + .inv_vs_pol = 1, + .inv_hs_pol = 1, + }; + static struct i2c_board_info adv7604_info = { + .type = "adv7604", + .addr = 0x20, + .platform_data = &adv7604_pdata, + }; + + struct cobalt_stream *s = cobalt->streams; + int i; + + for (i = 0; i < COBALT_NUM_INPUTS; i++) { + struct v4l2_subdev_format sd_fmt = { + .pad = ADV7604_PAD_SOURCE, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format.code = MEDIA_BUS_FMT_YUYV8_1X16, + }; + struct v4l2_subdev_edid cobalt_edid = { + .pad = ADV76XX_PAD_HDMI_PORT_A, + .start_block = 0, + .blocks = 2, + .edid = edid, + }; + int err; + + s[i].pad_source = ADV7604_PAD_SOURCE; + s[i].i2c_adap = &cobalt->i2c_adap[i]; + if (s[i].i2c_adap->dev.parent == NULL) + continue; + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(i), 1); + s[i].sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev, + s[i].i2c_adap, &adv7604_info, NULL); + if (!s[i].sd) { + if (cobalt_ignore_err) + continue; + return -ENODEV; + } + err = v4l2_subdev_call(s[i].sd, video, s_routing, + ADV76XX_PAD_HDMI_PORT_A, 0, 0); + if (err) + return err; + err = v4l2_subdev_call(s[i].sd, pad, set_edid, + &cobalt_edid); + if (err) + return err; + err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL, + &sd_fmt); + if (err) + return err; + /* Reset channel video module */ + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(i), 0); + mdelay(2); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(i), 1); + mdelay(1); + s[i].is_dummy = false; + cobalt->streams[i + COBALT_AUDIO_IN_STREAM].is_dummy = false; + } + return 0; +} + +static int cobalt_subdevs_hsma_init(struct cobalt *cobalt) +{ + static struct adv7842_platform_data adv7842_pdata = { + .disable_pwrdnb = 1, + .ain_sel = ADV7842_AIN1_2_3_NC_SYNC_1_2, + .bus_order = ADV7842_BUS_ORDER_RBG, + .op_format_mode_sel = ADV7842_OP_FORMAT_MODE0, + .blank_data = 1, + .op_656_range = 1, + .dr_str_data = 3, + .dr_str_clk = 3, + .dr_str_sync = 3, + .mode = ADV7842_MODE_HDMI, + .hdmi_free_run_enable = 1, + .vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P, + .i2c_sdp_io = 0x4a, + .i2c_sdp = 0x48, + .i2c_cp = 0x22, + .i2c_vdp = 0x24, + .i2c_afe = 0x26, + .i2c_hdmi = 0x34, + .i2c_repeater = 0x32, + .i2c_edid = 0x36, + .i2c_infoframe = 0x3e, + .i2c_cec = 0x40, + .i2c_avlink = 0x42, + }; + static struct i2c_board_info adv7842_info = { + .type = "adv7842", + .addr = 0x20, + .platform_data = &adv7842_pdata, + }; + static struct v4l2_subdev_format sd_fmt = { + .pad = ADV7842_PAD_SOURCE, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format.code = MEDIA_BUS_FMT_YUYV8_1X16, + }; + static struct adv7511_platform_data adv7511_pdata = { + .i2c_edid = 0x7e >> 1, + .i2c_cec = 0x7c >> 1, + .i2c_pktmem = 0x70 >> 1, + .cec_clk = 12000000, + }; + static struct i2c_board_info adv7511_info = { + .type = "adv7511", + .addr = 0x39, /* 0x39 or 0x3d */ + .platform_data = &adv7511_pdata, + }; + struct v4l2_subdev_edid cobalt_edid = { + .pad = ADV7842_EDID_PORT_A, + .start_block = 0, + .blocks = 2, + .edid = edid, + }; + struct cobalt_stream *s = &cobalt->streams[COBALT_HSMA_IN_NODE]; + + s->i2c_adap = &cobalt->i2c_adap[COBALT_NUM_ADAPTERS - 1]; + if (s->i2c_adap->dev.parent == NULL) + return 0; + cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(4), 1); + + s->sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev, + s->i2c_adap, &adv7842_info, NULL); + if (s->sd) { + int err = v4l2_subdev_call(s->sd, pad, set_edid, &cobalt_edid); + + if (err) + return err; + err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + &sd_fmt); + if (err) + return err; + cobalt->have_hsma_rx = true; + s->pad_source = ADV7842_PAD_SOURCE; + s->is_dummy = false; + cobalt->streams[4 + COBALT_AUDIO_IN_STREAM].is_dummy = false; + /* Reset channel video module */ + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 0); + mdelay(2); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 1); + mdelay(1); + return err; + } + cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(4), 0); + cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_PWRDN0_TO_HSMA_TX_BIT, 0); + s++; + s->i2c_adap = &cobalt->i2c_adap[COBALT_NUM_ADAPTERS - 1]; + s->sd = v4l2_i2c_new_subdev_board(&cobalt->v4l2_dev, + s->i2c_adap, &adv7511_info, NULL); + if (s->sd) { + /* A transmitter is hooked up, so we can set this bit */ + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 1); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(4), 0); + cobalt_s_bit_sysctrl(cobalt, + COBALT_SYS_CTRL_VIDEO_TX_RESETN_BIT, 1); + cobalt->have_hsma_tx = true; + v4l2_subdev_call(s->sd, core, s_power, 1); + v4l2_subdev_call(s->sd, video, s_stream, 1); + v4l2_subdev_call(s->sd, audio, s_stream, 1); + v4l2_ctrl_s_ctrl(v4l2_ctrl_find(s->sd->ctrl_handler, + V4L2_CID_DV_TX_MODE), V4L2_DV_TX_MODE_HDMI); + s->is_dummy = false; + cobalt->streams[COBALT_AUDIO_OUT_STREAM].is_dummy = false; + return 0; + } + return -ENODEV; +} + +static int cobalt_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cobalt *cobalt; + int retval = 0; + int i; + + /* FIXME - module parameter arrays constrain max instances */ + i = atomic_inc_return(&cobalt_instance) - 1; + + cobalt = kzalloc(sizeof(struct cobalt), GFP_ATOMIC); + if (cobalt == NULL) + return -ENOMEM; + cobalt->pci_dev = pci_dev; + cobalt->instance = i; + + cobalt->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); + if (IS_ERR(cobalt->alloc_ctx)) { + kfree(cobalt); + return -ENOMEM; + } + + retval = v4l2_device_register(&pci_dev->dev, &cobalt->v4l2_dev); + if (retval) { + pr_err("cobalt: v4l2_device_register of card %d failed\n", + cobalt->instance); + vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); + kfree(cobalt); + return retval; + } + snprintf(cobalt->v4l2_dev.name, sizeof(cobalt->v4l2_dev.name), + "cobalt-%d", cobalt->instance); + cobalt->v4l2_dev.notify = cobalt_notify; + cobalt_info("Initializing card %d\n", cobalt->instance); + + cobalt->irq_work_queues = + create_singlethread_workqueue(cobalt->v4l2_dev.name); + if (cobalt->irq_work_queues == NULL) { + cobalt_err("Could not create workqueue\n"); + retval = -ENOMEM; + goto err; + } + + INIT_WORK(&cobalt->irq_work_queue, cobalt_irq_work_handler); + + /* PCI Device Setup */ + retval = cobalt_setup_pci(cobalt, pci_dev, pci_id); + if (retval != 0) + goto err_wq; + + /* Show HDL version info */ + if (cobalt_hdl_info_get(cobalt)) + cobalt_info("Not able to read the HDL info\n"); + else + cobalt_info("%s", cobalt->hdl_info); + + retval = cobalt_i2c_init(cobalt); + if (retval) + goto err_pci; + + cobalt_stream_struct_init(cobalt); + + retval = cobalt_subdevs_init(cobalt); + if (retval) + goto err_i2c; + + if (!(cobalt_read_bar1(cobalt, COBALT_SYS_STAT_BASE) & + COBALT_SYSSTAT_HSMA_PRSNTN_MSK)) { + retval = cobalt_subdevs_hsma_init(cobalt); + if (retval) + goto err_i2c; + } + + retval = v4l2_device_register_subdev_nodes(&cobalt->v4l2_dev); + if (retval) + goto err_i2c; + retval = cobalt_nodes_register(cobalt); + if (retval) { + cobalt_err("Error %d registering device nodes\n", retval); + goto err_i2c; + } + cobalt_set_interrupt(cobalt, true); + v4l2_device_call_all(&cobalt->v4l2_dev, 0, core, + interrupt_service_routine, 0, NULL); + + cobalt_info("Initialized cobalt card\n"); + + cobalt_flash_probe(cobalt); + + return 0; + +err_i2c: + cobalt_i2c_exit(cobalt); + cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 0); +err_pci: + cobalt_free_msi(cobalt, pci_dev); + cobalt_pci_iounmap(cobalt, pci_dev); + pci_release_regions(cobalt->pci_dev); + pci_disable_device(cobalt->pci_dev); +err_wq: + destroy_workqueue(cobalt->irq_work_queues); +err: + if (retval == 0) + retval = -ENODEV; + cobalt_err("error %d on initialization\n", retval); + + v4l2_device_unregister(&cobalt->v4l2_dev); + vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); + kfree(cobalt); + return retval; +} + +static void cobalt_remove(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cobalt *cobalt = to_cobalt(v4l2_dev); + int i; + + cobalt_flash_remove(cobalt); + cobalt_set_interrupt(cobalt, false); + flush_workqueue(cobalt->irq_work_queues); + cobalt_nodes_unregister(cobalt); + for (i = 0; i < COBALT_NUM_ADAPTERS; i++) { + struct v4l2_subdev *sd = cobalt->streams[i].sd; + struct i2c_client *client; + + if (sd == NULL) + continue; + client = v4l2_get_subdevdata(sd); + v4l2_device_unregister_subdev(sd); + i2c_unregister_device(client); + } + cobalt_i2c_exit(cobalt); + cobalt_free_msi(cobalt, pci_dev); + cobalt_s_bit_sysctrl(cobalt, COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT, 0); + cobalt_pci_iounmap(cobalt, pci_dev); + pci_release_regions(cobalt->pci_dev); + pci_disable_device(cobalt->pci_dev); + destroy_workqueue(cobalt->irq_work_queues); + + cobalt_info("removed cobalt card\n"); + + v4l2_device_unregister(v4l2_dev); + vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx); + kfree(cobalt); +} + +/* define a pci_driver for card detection */ +static struct pci_driver cobalt_pci_driver = { + .name = "cobalt", + .id_table = cobalt_pci_tbl, + .probe = cobalt_probe, + .remove = cobalt_remove, +}; + +module_pci_driver(cobalt_pci_driver); diff --git a/kernel/drivers/media/pci/cobalt/cobalt-driver.h b/kernel/drivers/media/pci/cobalt/cobalt-driver.h new file mode 100644 index 000000000..b2f08e4a6 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-driver.h @@ -0,0 +1,382 @@ +/* + * cobalt driver internal defines and structures + * + * Derived from cx18-driver.h + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COBALT_DRIVER_H +#define COBALT_DRIVER_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "m00233_video_measure_memmap_package.h" +#include "m00235_fdma_packer_memmap_package.h" +#include "m00389_cvi_memmap_package.h" +#include "m00460_evcnt_memmap_package.h" +#include "m00473_freewheel_memmap_package.h" +#include "m00479_clk_loss_detector_memmap_package.h" +#include "m00514_syncgen_flow_evcnt_memmap_package.h" + +/* System device ID */ +#define PCI_DEVICE_ID_COBALT 0x2732 + +/* Number of cobalt device nodes. */ +#define COBALT_NUM_INPUTS 4 +#define COBALT_NUM_NODES 6 + +/* Number of cobalt device streams. */ +#define COBALT_NUM_STREAMS 12 + +#define COBALT_HSMA_IN_NODE 4 +#define COBALT_HSMA_OUT_NODE 5 + +/* Cobalt audio streams */ +#define COBALT_AUDIO_IN_STREAM 6 +#define COBALT_AUDIO_OUT_STREAM 11 + +/* DMA stuff */ +#define DMA_CHANNELS_MAX 16 + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 +#define COBALT_NUM_ADAPTERS 5 + +#define COBALT_CLK 50000000 + +/* System status register */ +#define COBALT_SYSSTAT_DIP0_MSK (1 << 0) +#define COBALT_SYSSTAT_DIP1_MSK (1 << 1) +#define COBALT_SYSSTAT_HSMA_PRSNTN_MSK (1 << 2) +#define COBALT_SYSSTAT_FLASH_RDYBSYN_MSK (1 << 3) +#define COBALT_SYSSTAT_VI0_5V_MSK (1 << 4) +#define COBALT_SYSSTAT_VI0_INT1_MSK (1 << 5) +#define COBALT_SYSSTAT_VI0_INT2_MSK (1 << 6) +#define COBALT_SYSSTAT_VI0_LOST_DATA_MSK (1 << 7) +#define COBALT_SYSSTAT_VI1_5V_MSK (1 << 8) +#define COBALT_SYSSTAT_VI1_INT1_MSK (1 << 9) +#define COBALT_SYSSTAT_VI1_INT2_MSK (1 << 10) +#define COBALT_SYSSTAT_VI1_LOST_DATA_MSK (1 << 11) +#define COBALT_SYSSTAT_VI2_5V_MSK (1 << 12) +#define COBALT_SYSSTAT_VI2_INT1_MSK (1 << 13) +#define COBALT_SYSSTAT_VI2_INT2_MSK (1 << 14) +#define COBALT_SYSSTAT_VI2_LOST_DATA_MSK (1 << 15) +#define COBALT_SYSSTAT_VI3_5V_MSK (1 << 16) +#define COBALT_SYSSTAT_VI3_INT1_MSK (1 << 17) +#define COBALT_SYSSTAT_VI3_INT2_MSK (1 << 18) +#define COBALT_SYSSTAT_VI3_LOST_DATA_MSK (1 << 19) +#define COBALT_SYSSTAT_VIHSMA_5V_MSK (1 << 20) +#define COBALT_SYSSTAT_VIHSMA_INT1_MSK (1 << 21) +#define COBALT_SYSSTAT_VIHSMA_INT2_MSK (1 << 22) +#define COBALT_SYSSTAT_VIHSMA_LOST_DATA_MSK (1 << 23) +#define COBALT_SYSSTAT_VOHSMA_INT1_MSK (1 << 24) +#define COBALT_SYSSTAT_VOHSMA_PLL_LOCKED_MSK (1 << 25) +#define COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK (1 << 26) +#define COBALT_SYSSTAT_AUD_PLL_LOCKED_MSK (1 << 28) +#define COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK (1 << 29) +#define COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK (1 << 30) +#define COBALT_SYSSTAT_PCIE_SMBCLK_MSK (1 << 31) + +/* Cobalt memory map */ +#define COBALT_I2C_0_BASE 0x0 +#define COBALT_I2C_1_BASE 0x080 +#define COBALT_I2C_2_BASE 0x100 +#define COBALT_I2C_3_BASE 0x180 +#define COBALT_I2C_HSMA_BASE 0x200 + +#define COBALT_SYS_CTRL_BASE 0x400 +#define COBALT_SYS_CTRL_HSMA_TX_ENABLE_BIT 1 +#define COBALT_SYS_CTRL_VIDEO_RX_RESETN_BIT(n) (4 + 4 * (n)) +#define COBALT_SYS_CTRL_NRESET_TO_HDMI_BIT(n) (5 + 4 * (n)) +#define COBALT_SYS_CTRL_HPD_TO_CONNECTOR_BIT(n) (6 + 4 * (n)) +#define COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(n) (7 + 4 * (n)) +#define COBALT_SYS_CTRL_PWRDN0_TO_HSMA_TX_BIT 24 +#define COBALT_SYS_CTRL_VIDEO_TX_RESETN_BIT 25 +#define COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT 27 + +#define COBALT_SYS_STAT_BASE 0x500 +#define COBALT_SYS_STAT_MASK (COBALT_SYS_STAT_BASE + 0x08) +#define COBALT_SYS_STAT_EDGE (COBALT_SYS_STAT_BASE + 0x0c) + +#define COBALT_HDL_INFO_BASE 0x4800 +#define COBALT_HDL_INFO_SIZE 0x200 + +#define COBALT_VID_BASE 0x10000 +#define COBALT_VID_SIZE 0x1000 + +#define COBALT_CVI(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE) +#define COBALT_CVI_VMR(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE + 0x100) +#define COBALT_CVI_EVCNT(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE + 0x200) +#define COBALT_CVI_FREEWHEEL(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE + 0x300) +#define COBALT_CVI_CLK_LOSS(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE + 0x400) +#define COBALT_CVI_PACKER(cobalt, c) \ + (cobalt->bar1 + COBALT_VID_BASE + (c) * COBALT_VID_SIZE + 0x500) + +#define COBALT_TX_BASE(cobalt) (cobalt->bar1 + COBALT_VID_BASE + 0x5000) + +#define DMA_INTERRUPT_STATUS_REG 0x08 + +#define COBALT_HDL_SEARCH_STR "** HDL version info **" + +/* Cobalt CPU bus interface */ +#define COBALT_BUS_BAR1_BASE 0x600 +#define COBALT_BUS_SRAM_BASE 0x0 +#define COBALT_BUS_CPLD_BASE 0x00600000 +#define COBALT_BUS_FLASH_BASE 0x08000000 + +/* FDMA to PCIe packing */ +#define COBALT_BYTES_PER_PIXEL_YUYV 2 +#define COBALT_BYTES_PER_PIXEL_RGB24 3 +#define COBALT_BYTES_PER_PIXEL_RGB32 4 + +/* debugging */ +extern int cobalt_debug; +extern int cobalt_ignore_err; + +#define cobalt_err(fmt, arg...) v4l2_err(&cobalt->v4l2_dev, fmt, ## arg) +#define cobalt_warn(fmt, arg...) v4l2_warn(&cobalt->v4l2_dev, fmt, ## arg) +#define cobalt_info(fmt, arg...) v4l2_info(&cobalt->v4l2_dev, fmt, ## arg) +#define cobalt_dbg(level, fmt, arg...) \ + v4l2_dbg(level, cobalt_debug, &cobalt->v4l2_dev, fmt, ## arg) + +struct cobalt; +struct cobalt_i2c_regs; + +/* Per I2C bus private algo callback data */ +struct cobalt_i2c_data { + struct cobalt *cobalt; + struct cobalt_i2c_regs __iomem *regs; +}; + +struct pci_consistent_buffer { + void *virt; + dma_addr_t bus; + size_t bytes; +}; + +struct sg_dma_desc_info { + void *virt; + dma_addr_t bus; + unsigned size; + void *last_desc_virt; + struct device *dev; +}; + +#define COBALT_MAX_WIDTH 1920 +#define COBALT_MAX_HEIGHT 1200 +#define COBALT_MAX_BPP 3 +#define COBALT_MAX_FRAMESZ \ + (COBALT_MAX_WIDTH * COBALT_MAX_HEIGHT * COBALT_MAX_BPP) + +#define NR_BUFS VIDEO_MAX_FRAME + +#define COBALT_STREAM_FL_DMA_IRQ 0 +#define COBALT_STREAM_FL_ADV_IRQ 1 + +struct cobalt_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +static inline +struct cobalt_buffer *to_cobalt_buffer(struct vb2_v4l2_buffer *vb2) +{ + return container_of(vb2, struct cobalt_buffer, vb); +} + +struct cobalt_stream { + struct video_device vdev; + struct vb2_queue q; + struct list_head bufs; + struct i2c_adapter *i2c_adap; + struct v4l2_subdev *sd; + struct mutex lock; + spinlock_t irqlock; + struct v4l2_dv_timings timings; + u32 input; + u32 pad_source; + u32 width, height, bpp; + u32 stride; + u32 pixfmt; + u32 sequence; + u32 colorspace; + u32 xfer_func; + u32 ycbcr_enc; + u32 quantization; + + u8 dma_channel; + int video_channel; + unsigned dma_fifo_mask; + unsigned adv_irq_mask; + struct sg_dma_desc_info dma_desc_info[NR_BUFS]; + unsigned long flags; + bool unstable_frame; + bool enable_cvi; + bool enable_freewheel; + unsigned skip_first_frames; + bool is_output; + bool is_audio; + bool is_dummy; + + struct cobalt *cobalt; + struct snd_cobalt_card *alsa; +}; + +struct snd_cobalt_card; + +/* Struct to hold info about cobalt cards */ +struct cobalt { + int instance; + struct pci_dev *pci_dev; + struct v4l2_device v4l2_dev; + void *alloc_ctx; + + void __iomem *bar0, *bar1; + + u8 card_rev; + u16 device_id; + + /* device nodes */ + struct cobalt_stream streams[DMA_CHANNELS_MAX]; + struct i2c_adapter i2c_adap[COBALT_NUM_ADAPTERS]; + struct cobalt_i2c_data i2c_data[COBALT_NUM_ADAPTERS]; + bool have_hsma_rx; + bool have_hsma_tx; + + /* irq */ + struct workqueue_struct *irq_work_queues; + struct work_struct irq_work_queue; /* work entry */ + /* irq counters */ + u32 irq_adv1; + u32 irq_adv2; + u32 irq_advout; + u32 irq_dma_tot; + u32 irq_dma[COBALT_NUM_STREAMS]; + u32 irq_none; + u32 irq_full_fifo; + + bool msi_enabled; + + /* omnitek dma */ + int dma_channels; + int first_fifo_channel; + bool pci_32_bit; + + char hdl_info[COBALT_HDL_INFO_SIZE]; + + /* NOR flash */ + struct mtd_info *mtd; +}; + +static inline struct cobalt *to_cobalt(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cobalt, v4l2_dev); +} + +static inline void cobalt_write_bar0(struct cobalt *cobalt, u32 reg, u32 val) +{ + iowrite32(val, cobalt->bar0 + reg); +} + +static inline u32 cobalt_read_bar0(struct cobalt *cobalt, u32 reg) +{ + return ioread32(cobalt->bar0 + reg); +} + +static inline void cobalt_write_bar1(struct cobalt *cobalt, u32 reg, u32 val) +{ + iowrite32(val, cobalt->bar1 + reg); +} + +static inline u32 cobalt_read_bar1(struct cobalt *cobalt, u32 reg) +{ + return ioread32(cobalt->bar1 + reg); +} + +static inline u32 cobalt_g_sysctrl(struct cobalt *cobalt) +{ + return cobalt_read_bar1(cobalt, COBALT_SYS_CTRL_BASE); +} + +static inline void cobalt_s_bit_sysctrl(struct cobalt *cobalt, + int bit, int val) +{ + u32 ctrl = cobalt_read_bar1(cobalt, COBALT_SYS_CTRL_BASE); + + cobalt_write_bar1(cobalt, COBALT_SYS_CTRL_BASE, + (ctrl & ~(1UL << bit)) | (val << bit)); +} + +static inline u32 cobalt_g_sysstat(struct cobalt *cobalt) +{ + return cobalt_read_bar1(cobalt, COBALT_SYS_STAT_BASE); +} + +#define ADRS_REG (bar1 + COBALT_BUS_BAR1_BASE + 0) +#define LOWER_DATA (bar1 + COBALT_BUS_BAR1_BASE + 4) +#define UPPER_DATA (bar1 + COBALT_BUS_BAR1_BASE + 6) + +static inline u32 cobalt_bus_read32(void __iomem *bar1, u32 bus_adrs) +{ + iowrite32(bus_adrs, ADRS_REG); + return ioread32(LOWER_DATA); +} + +static inline void cobalt_bus_write16(void __iomem *bar1, + u32 bus_adrs, u16 data) +{ + iowrite32(bus_adrs, ADRS_REG); + if (bus_adrs & 2) + iowrite16(data, UPPER_DATA); + else + iowrite16(data, LOWER_DATA); +} + +static inline void cobalt_bus_write32(void __iomem *bar1, + u32 bus_adrs, u16 data) +{ + iowrite32(bus_adrs, ADRS_REG); + if (bus_adrs & 2) + iowrite32(data, UPPER_DATA); + else + iowrite32(data, LOWER_DATA); +} + +/*==============Prototypes==================*/ + +void cobalt_pcie_status_show(struct cobalt *cobalt); + +#endif diff --git a/kernel/drivers/media/pci/cobalt/cobalt-flash.c b/kernel/drivers/media/pci/cobalt/cobalt-flash.c new file mode 100644 index 000000000..04dcaf919 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-flash.c @@ -0,0 +1,128 @@ +/* + * Cobalt NOR flash functions + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "cobalt-flash.h" + +#define ADRS(offset) (COBALT_BUS_FLASH_BASE + offset) + +static struct map_info cobalt_flash_map = { + .name = "cobalt-flash", + .bankwidth = 2, /* 16 bits */ + .size = 0x4000000, /* 64MB */ + .phys = 0, /* offset */ +}; + +static map_word flash_read16(struct map_info *map, unsigned long offset) +{ + map_word r; + + r.x[0] = cobalt_bus_read32(map->virt, ADRS(offset)); + if (offset & 0x2) + r.x[0] >>= 16; + else + r.x[0] &= 0x0000ffff; + + return r; +} + +static void flash_write16(struct map_info *map, const map_word datum, + unsigned long offset) +{ + u16 data = (u16)datum.x[0]; + + cobalt_bus_write16(map->virt, ADRS(offset), data); +} + +static void flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + u32 src = from; + u8 *dest = to; + u32 data; + + while (len) { + data = cobalt_bus_read32(map->virt, ADRS(src)); + do { + *dest = data >> (8 * (src & 3)); + src++; + dest++; + len--; + } while (len && (src % 4)); + } +} + +static void flash_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + const u8 *src = from; + u32 dest = to; + + pr_info("%s: offset 0x%x: length %zu\n", __func__, dest, len); + while (len) { + u16 data = 0xffff; + + do { + data = *src << (8 * (dest & 1)); + src++; + dest++; + len--; + } while (len && (dest % 2)); + + cobalt_bus_write16(map->virt, ADRS(dest - 2), data); + } +} + +int cobalt_flash_probe(struct cobalt *cobalt) +{ + struct map_info *map = &cobalt_flash_map; + struct mtd_info *mtd; + + BUG_ON(!map_bankwidth_supported(map->bankwidth)); + map->virt = cobalt->bar1; + map->read = flash_read16; + map->write = flash_write16; + map->copy_from = flash_copy_from; + map->copy_to = flash_copy_to; + + mtd = do_map_probe("cfi_probe", map); + cobalt->mtd = mtd; + if (!mtd) { + cobalt_err("Probe CFI flash failed!\n"); + return -1; + } + + mtd->owner = THIS_MODULE; + mtd->dev.parent = &cobalt->pci_dev->dev; + mtd_device_register(mtd, NULL, 0); + return 0; +} + +void cobalt_flash_remove(struct cobalt *cobalt) +{ + if (cobalt->mtd) { + mtd_device_unregister(cobalt->mtd); + map_destroy(cobalt->mtd); + } +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-flash.h b/kernel/drivers/media/pci/cobalt/cobalt-flash.h new file mode 100644 index 000000000..8077daea5 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-flash.h @@ -0,0 +1,29 @@ +/* + * Cobalt NOR flash functions + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COBALT_FLASH_H +#define COBALT_FLASH_H + +#include "cobalt-driver.h" + +int cobalt_flash_probe(struct cobalt *cobalt); +void cobalt_flash_remove(struct cobalt *cobalt); + +#endif diff --git a/kernel/drivers/media/pci/cobalt/cobalt-i2c.c b/kernel/drivers/media/pci/cobalt/cobalt-i2c.c new file mode 100644 index 000000000..ad16b89b8 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-i2c.c @@ -0,0 +1,396 @@ +/* + * cobalt I2C functions + * + * Derived from cx18-i2c.c + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "cobalt-driver.h" +#include "cobalt-i2c.h" + +struct cobalt_i2c_regs { + /* Clock prescaler register lo-byte */ + u8 prerlo; + u8 dummy0[3]; + /* Clock prescaler register high-byte */ + u8 prerhi; + u8 dummy1[3]; + /* Control register */ + u8 ctr; + u8 dummy2[3]; + /* Transmit/Receive register */ + u8 txr_rxr; + u8 dummy3[3]; + /* Command and Status register */ + u8 cr_sr; + u8 dummy4[3]; +}; + +/* CTR[7:0] - Control register */ + +/* I2C Core enable bit */ +#define M00018_CTR_BITMAP_EN_MSK (1 << 7) + +/* I2C Core interrupt enable bit */ +#define M00018_CTR_BITMAP_IEN_MSK (1 << 6) + +/* CR[7:0] - Command register */ + +/* I2C start condition */ +#define M00018_CR_BITMAP_STA_MSK (1 << 7) + +/* I2C stop condition */ +#define M00018_CR_BITMAP_STO_MSK (1 << 6) + +/* I2C read from slave */ +#define M00018_CR_BITMAP_RD_MSK (1 << 5) + +/* I2C write to slave */ +#define M00018_CR_BITMAP_WR_MSK (1 << 4) + +/* I2C ack */ +#define M00018_CR_BITMAP_ACK_MSK (1 << 3) + +/* I2C Interrupt ack */ +#define M00018_CR_BITMAP_IACK_MSK (1 << 0) + +/* SR[7:0] - Status register */ + +/* Receive acknowledge from slave */ +#define M00018_SR_BITMAP_RXACK_MSK (1 << 7) + +/* Busy, I2C bus busy (as defined by start / stop bits) */ +#define M00018_SR_BITMAP_BUSY_MSK (1 << 6) + +/* Arbitration lost - core lost arbitration */ +#define M00018_SR_BITMAP_AL_MSK (1 << 5) + +/* Transfer in progress */ +#define M00018_SR_BITMAP_TIP_MSK (1 << 1) + +/* Interrupt flag */ +#define M00018_SR_BITMAP_IF_MSK (1 << 0) + +/* Frequency, in Hz */ +#define I2C_FREQUENCY 400000 +#define ALT_CPU_FREQ 83333333 + +static struct cobalt_i2c_regs __iomem * +cobalt_i2c_regs(struct cobalt *cobalt, unsigned idx) +{ + switch (idx) { + case 0: + default: + return (struct cobalt_i2c_regs __iomem *) + (cobalt->bar1 + COBALT_I2C_0_BASE); + case 1: + return (struct cobalt_i2c_regs __iomem *) + (cobalt->bar1 + COBALT_I2C_1_BASE); + case 2: + return (struct cobalt_i2c_regs __iomem *) + (cobalt->bar1 + COBALT_I2C_2_BASE); + case 3: + return (struct cobalt_i2c_regs __iomem *) + (cobalt->bar1 + COBALT_I2C_3_BASE); + case 4: + return (struct cobalt_i2c_regs __iomem *) + (cobalt->bar1 + COBALT_I2C_HSMA_BASE); + } +} + +/* Do low-level i2c byte transfer. + * Returns -1 in case of an error or 0 otherwise. + */ +static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs, + struct i2c_adapter *adap, bool start, bool stop, + u8 *data, u16 len) +{ + unsigned long start_time; + int status; + int cmd; + int i; + + for (i = 0; i < len; i++) { + /* Setup data */ + iowrite8(data[i], ®s->txr_rxr); + + /* Setup command */ + if (i == 0 && start != 0) { + /* Write + Start */ + cmd = M00018_CR_BITMAP_WR_MSK | + M00018_CR_BITMAP_STA_MSK; + } else if (i == len - 1 && stop != 0) { + /* Write + Stop */ + cmd = M00018_CR_BITMAP_WR_MSK | + M00018_CR_BITMAP_STO_MSK; + } else { + /* Write only */ + cmd = M00018_CR_BITMAP_WR_MSK; + } + + /* Execute command */ + iowrite8(cmd, ®s->cr_sr); + + /* Wait for transfer to complete (TIP = 0) */ + start_time = jiffies; + status = ioread8(®s->cr_sr); + while (status & M00018_SR_BITMAP_TIP_MSK) { + if (time_after(jiffies, start_time + adap->timeout)) + return -ETIMEDOUT; + cond_resched(); + status = ioread8(®s->cr_sr); + } + + /* Verify ACK */ + if (status & M00018_SR_BITMAP_RXACK_MSK) { + /* NO ACK! */ + return -EIO; + } + + /* Verify arbitration */ + if (status & M00018_SR_BITMAP_AL_MSK) { + /* Arbitration lost! */ + return -EIO; + } + } + return 0; +} + +/* Do low-level i2c byte read. + * Returns -1 in case of an error or 0 otherwise. + */ +static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs, + struct i2c_adapter *adap, bool start, bool stop, + u8 *data, u16 len) +{ + unsigned long start_time; + int status; + int cmd; + int i; + + for (i = 0; i < len; i++) { + /* Setup command */ + if (i == 0 && start != 0) { + /* Read + Start */ + cmd = M00018_CR_BITMAP_RD_MSK | + M00018_CR_BITMAP_STA_MSK; + } else if (i == len - 1 && stop != 0) { + /* Read + Stop */ + cmd = M00018_CR_BITMAP_RD_MSK | + M00018_CR_BITMAP_STO_MSK; + } else { + /* Read only */ + cmd = M00018_CR_BITMAP_RD_MSK; + } + + /* Last byte to read, no ACK */ + if (i == len - 1) + cmd |= M00018_CR_BITMAP_ACK_MSK; + + /* Execute command */ + iowrite8(cmd, ®s->cr_sr); + + /* Wait for transfer to complete (TIP = 0) */ + start_time = jiffies; + status = ioread8(®s->cr_sr); + while (status & M00018_SR_BITMAP_TIP_MSK) { + if (time_after(jiffies, start_time + adap->timeout)) + return -ETIMEDOUT; + cond_resched(); + status = ioread8(®s->cr_sr); + } + + /* Verify arbitration */ + if (status & M00018_SR_BITMAP_AL_MSK) { + /* Arbitration lost! */ + return -EIO; + } + + /* Store data */ + data[i] = ioread8(®s->txr_rxr); + } + return 0; +} + +/* Generate stop condition on i2c bus. + * The m00018 stop isn't doing the right thing (wrong timing). + * So instead send a start condition, 8 zeroes and a stop condition. + */ +static int cobalt_stop(struct cobalt_i2c_regs __iomem *regs, + struct i2c_adapter *adap) +{ + u8 data = 0; + + return cobalt_tx_bytes(regs, adap, true, true, &data, 1); +} + +static int cobalt_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct cobalt_i2c_data *data = adap->algo_data; + struct cobalt_i2c_regs __iomem *regs = data->regs; + struct i2c_msg *pmsg; + unsigned short flags; + int ret = 0; + int i, j; + + for (i = 0; i < num; i++) { + int stop = (i == num - 1); + + pmsg = &msgs[i]; + flags = pmsg->flags; + + if (!(pmsg->flags & I2C_M_NOSTART)) { + u8 addr = pmsg->addr << 1; + + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + for (j = 0; j < adap->retries; j++) { + ret = cobalt_tx_bytes(regs, adap, true, false, + &addr, 1); + if (!ret) + break; + cobalt_stop(regs, adap); + } + if (ret < 0) + return ret; + ret = 0; + } + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer */ + ret = cobalt_rx_bytes(regs, adap, false, stop, + pmsg->buf, pmsg->len); + if (ret < 0) + goto bailout; + } else { + /* write bytes from buffer */ + ret = cobalt_tx_bytes(regs, adap, false, stop, + pmsg->buf, pmsg->len); + if (ret < 0) + goto bailout; + } + } + ret = i; + +bailout: + if (ret < 0) + cobalt_stop(regs, adap); + return ret; +} + +static u32 cobalt_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* template for i2c-bit-algo */ +static struct i2c_adapter cobalt_i2c_adap_template = { + .name = "cobalt i2c driver", + .algo = NULL, /* set by i2c-algo-bit */ + .algo_data = NULL, /* filled from template */ + .owner = THIS_MODULE, +}; + +static const struct i2c_algorithm cobalt_algo = { + .master_xfer = cobalt_xfer, + .functionality = cobalt_func, +}; + +/* init + register i2c algo-bit adapter */ +int cobalt_i2c_init(struct cobalt *cobalt) +{ + int i, err; + int status; + int prescale; + unsigned long start_time; + + cobalt_dbg(1, "i2c init\n"); + + /* Define I2C clock prescaler */ + prescale = ((ALT_CPU_FREQ) / (5 * I2C_FREQUENCY)) - 1; + + for (i = 0; i < COBALT_NUM_ADAPTERS; i++) { + struct cobalt_i2c_regs __iomem *regs = + cobalt_i2c_regs(cobalt, i); + struct i2c_adapter *adap = &cobalt->i2c_adap[i]; + + /* Disable I2C */ + iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->cr_sr); + iowrite8(0, ®s->ctr); + iowrite8(0, ®s->cr_sr); + + start_time = jiffies; + do { + if (time_after(jiffies, start_time + HZ)) { + if (cobalt_ignore_err) { + adap->dev.parent = NULL; + return 0; + } + return -ETIMEDOUT; + } + status = ioread8(®s->cr_sr); + } while (status & M00018_SR_BITMAP_TIP_MSK); + + /* Disable I2C */ + iowrite8(0, ®s->ctr); + iowrite8(0, ®s->cr_sr); + + /* Calculate i2c prescaler */ + iowrite8(prescale & 0xff, ®s->prerlo); + iowrite8((prescale >> 8) & 0xff, ®s->prerhi); + /* Enable I2C, interrupts disabled */ + iowrite8(M00018_CTR_BITMAP_EN_MSK, ®s->ctr); + /* Setup algorithm for adapter */ + cobalt->i2c_data[i].cobalt = cobalt; + cobalt->i2c_data[i].regs = regs; + *adap = cobalt_i2c_adap_template; + adap->algo = &cobalt_algo; + adap->algo_data = &cobalt->i2c_data[i]; + adap->retries = 3; + sprintf(adap->name + strlen(adap->name), + " #%d-%d", cobalt->instance, i); + i2c_set_adapdata(adap, &cobalt->v4l2_dev); + adap->dev.parent = &cobalt->pci_dev->dev; + err = i2c_add_adapter(adap); + if (err) { + if (cobalt_ignore_err) { + adap->dev.parent = NULL; + return 0; + } + while (i--) + i2c_del_adapter(&cobalt->i2c_adap[i]); + return err; + } + cobalt_info("registered bus %s\n", adap->name); + } + return 0; +} + +void cobalt_i2c_exit(struct cobalt *cobalt) +{ + int i; + + cobalt_dbg(1, "i2c exit\n"); + + for (i = 0; i < COBALT_NUM_ADAPTERS; i++) { + cobalt_err("unregistered bus %s\n", cobalt->i2c_adap[i].name); + i2c_del_adapter(&cobalt->i2c_adap[i]); + } +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-i2c.h b/kernel/drivers/media/pci/cobalt/cobalt-i2c.h new file mode 100644 index 000000000..a4c1cfaac --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-i2c.h @@ -0,0 +1,25 @@ +/* + * cobalt I2C functions + * + * Derived from cx18-i2c.h + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* init + register i2c algo-bit adapter */ +int cobalt_i2c_init(struct cobalt *cobalt); +void cobalt_i2c_exit(struct cobalt *cobalt); diff --git a/kernel/drivers/media/pci/cobalt/cobalt-irq.c b/kernel/drivers/media/pci/cobalt/cobalt-irq.c new file mode 100644 index 000000000..3de26d071 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-irq.c @@ -0,0 +1,259 @@ +/* + * cobalt interrupt handling + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "cobalt-driver.h" +#include "cobalt-irq.h" +#include "cobalt-omnitek.h" + +static void cobalt_dma_stream_queue_handler(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + int rx = s->video_channel; + struct m00473_freewheel_regmap __iomem *fw = + COBALT_CVI_FREEWHEEL(s->cobalt, rx); + struct m00233_video_measure_regmap __iomem *vmr = + COBALT_CVI_VMR(s->cobalt, rx); + struct m00389_cvi_regmap __iomem *cvi = + COBALT_CVI(s->cobalt, rx); + struct m00479_clk_loss_detector_regmap __iomem *clkloss = + COBALT_CVI_CLK_LOSS(s->cobalt, rx); + struct cobalt_buffer *cb; + bool skip = false; + + spin_lock(&s->irqlock); + + if (list_empty(&s->bufs)) { + pr_err("no buffers!\n"); + spin_unlock(&s->irqlock); + return; + } + + /* Give the fresh filled up buffer to the user. + * Note that the interrupt is only sent if the DMA can continue + * with a new buffer, so it is always safe to return this buffer + * to userspace. */ + cb = list_first_entry(&s->bufs, struct cobalt_buffer, list); + list_del(&cb->list); + spin_unlock(&s->irqlock); + + if (s->is_audio || s->is_output) + goto done; + + if (s->unstable_frame) { + uint32_t stat = ioread32(&vmr->irq_status); + + iowrite32(stat, &vmr->irq_status); + if (!(ioread32(&vmr->status) & + M00233_STATUS_BITMAP_INIT_DONE_MSK)) { + cobalt_dbg(1, "!init_done\n"); + if (s->enable_freewheel) + goto restart_fw; + goto done; + } + + if (ioread32(&clkloss->status) & + M00479_STATUS_BITMAP_CLOCK_MISSING_MSK) { + iowrite32(0, &clkloss->ctrl); + iowrite32(M00479_CTRL_BITMAP_ENABLE_MSK, &clkloss->ctrl); + cobalt_dbg(1, "no clock\n"); + if (s->enable_freewheel) + goto restart_fw; + goto done; + } + if ((stat & (M00233_IRQ_STATUS_BITMAP_VACTIVE_AREA_MSK | + M00233_IRQ_STATUS_BITMAP_HACTIVE_AREA_MSK)) || + ioread32(&vmr->vactive_area) != s->timings.bt.height || + ioread32(&vmr->hactive_area) != s->timings.bt.width) { + cobalt_dbg(1, "unstable\n"); + if (s->enable_freewheel) + goto restart_fw; + goto done; + } + if (!s->enable_cvi) { + s->enable_cvi = true; + iowrite32(M00389_CONTROL_BITMAP_ENABLE_MSK, &cvi->control); + goto done; + } + if (!(ioread32(&cvi->status) & M00389_STATUS_BITMAP_LOCK_MSK)) { + cobalt_dbg(1, "cvi no lock\n"); + if (s->enable_freewheel) + goto restart_fw; + goto done; + } + if (!s->enable_freewheel) { + cobalt_dbg(1, "stable\n"); + s->enable_freewheel = true; + iowrite32(0, &fw->ctrl); + goto done; + } + cobalt_dbg(1, "enabled fw\n"); + iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK | + M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK, + &vmr->control); + iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK, &fw->ctrl); + s->enable_freewheel = false; + s->unstable_frame = false; + s->skip_first_frames = 2; + skip = true; + goto done; + } + if (ioread32(&fw->status) & M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK) { +restart_fw: + cobalt_dbg(1, "lost lock\n"); + iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, + &vmr->control); + iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK | + M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK, + &fw->ctrl); + iowrite32(0, &cvi->control); + s->unstable_frame = true; + s->enable_freewheel = false; + s->enable_cvi = false; + } +done: + if (s->skip_first_frames) { + skip = true; + s->skip_first_frames--; + } + v4l2_get_timestamp(&cb->vb.timestamp); + /* TODO: the sequence number should be read from the FPGA so we + also know about dropped frames. */ + cb->vb.sequence = s->sequence++; + vb2_buffer_done(&cb->vb.vb2_buf, + (skip || s->unstable_frame) ? + VB2_BUF_STATE_REQUEUEING : VB2_BUF_STATE_DONE); +} + +irqreturn_t cobalt_irq_handler(int irq, void *dev_id) +{ + struct cobalt *cobalt = (struct cobalt *)dev_id; + u32 dma_interrupt = + cobalt_read_bar0(cobalt, DMA_INTERRUPT_STATUS_REG) & 0xffff; + u32 mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK); + u32 edge = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_EDGE); + int i; + + /* Clear DMA interrupt */ + cobalt_write_bar0(cobalt, DMA_INTERRUPT_STATUS_REG, dma_interrupt); + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, mask & ~edge); + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_EDGE, edge); + + for (i = 0; i < COBALT_NUM_STREAMS; i++) { + struct cobalt_stream *s = &cobalt->streams[i]; + unsigned dma_fifo_mask = s->dma_fifo_mask; + + if (dma_interrupt & (1 << s->dma_channel)) { + cobalt->irq_dma[i]++; + /* Give fresh buffer to user and chain newly + * queued buffers */ + cobalt_dma_stream_queue_handler(s); + if (!s->is_audio) { + edge &= ~dma_fifo_mask; + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, + mask & ~edge); + } + } + if (s->is_audio) + continue; + if (edge & s->adv_irq_mask) + set_bit(COBALT_STREAM_FL_ADV_IRQ, &s->flags); + if ((edge & mask & dma_fifo_mask) && vb2_is_streaming(&s->q)) { + cobalt_info("full rx FIFO %d\n", i); + cobalt->irq_full_fifo++; + } + } + + queue_work(cobalt->irq_work_queues, &cobalt->irq_work_queue); + + if (edge & mask & (COBALT_SYSSTAT_VI0_INT1_MSK | + COBALT_SYSSTAT_VI1_INT1_MSK | + COBALT_SYSSTAT_VI2_INT1_MSK | + COBALT_SYSSTAT_VI3_INT1_MSK | + COBALT_SYSSTAT_VIHSMA_INT1_MSK | + COBALT_SYSSTAT_VOHSMA_INT1_MSK)) + cobalt->irq_adv1++; + if (edge & mask & (COBALT_SYSSTAT_VI0_INT2_MSK | + COBALT_SYSSTAT_VI1_INT2_MSK | + COBALT_SYSSTAT_VI2_INT2_MSK | + COBALT_SYSSTAT_VI3_INT2_MSK | + COBALT_SYSSTAT_VIHSMA_INT2_MSK)) + cobalt->irq_adv2++; + if (edge & mask & COBALT_SYSSTAT_VOHSMA_INT1_MSK) + cobalt->irq_advout++; + if (dma_interrupt) + cobalt->irq_dma_tot++; + if (!(edge & mask) && !dma_interrupt) + cobalt->irq_none++; + dma_interrupt = cobalt_read_bar0(cobalt, DMA_INTERRUPT_STATUS_REG); + + return IRQ_HANDLED; +} + +void cobalt_irq_work_handler(struct work_struct *work) +{ + struct cobalt *cobalt = + container_of(work, struct cobalt, irq_work_queue); + int i; + + for (i = 0; i < COBALT_NUM_NODES; i++) { + struct cobalt_stream *s = &cobalt->streams[i]; + + if (test_and_clear_bit(COBALT_STREAM_FL_ADV_IRQ, &s->flags)) { + u32 mask; + + v4l2_subdev_call(cobalt->streams[i].sd, core, + interrupt_service_routine, 0, NULL); + mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK); + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, + mask | s->adv_irq_mask); + } + } +} + +void cobalt_irq_log_status(struct cobalt *cobalt) +{ + u32 mask; + int i; + + cobalt_info("irq: adv1=%u adv2=%u advout=%u none=%u full=%u\n", + cobalt->irq_adv1, cobalt->irq_adv2, cobalt->irq_advout, + cobalt->irq_none, cobalt->irq_full_fifo); + cobalt_info("irq: dma_tot=%u (", cobalt->irq_dma_tot); + for (i = 0; i < COBALT_NUM_STREAMS; i++) + pr_cont("%s%u", i ? "/" : "", cobalt->irq_dma[i]); + pr_cont(")\n"); + cobalt->irq_dma_tot = cobalt->irq_adv1 = cobalt->irq_adv2 = 0; + cobalt->irq_advout = cobalt->irq_none = cobalt->irq_full_fifo = 0; + memset(cobalt->irq_dma, 0, sizeof(cobalt->irq_dma)); + + mask = cobalt_read_bar1(cobalt, COBALT_SYS_STAT_MASK); + cobalt_write_bar1(cobalt, COBALT_SYS_STAT_MASK, + mask | + COBALT_SYSSTAT_VI0_LOST_DATA_MSK | + COBALT_SYSSTAT_VI1_LOST_DATA_MSK | + COBALT_SYSSTAT_VI2_LOST_DATA_MSK | + COBALT_SYSSTAT_VI3_LOST_DATA_MSK | + COBALT_SYSSTAT_VIHSMA_LOST_DATA_MSK | + COBALT_SYSSTAT_VOHSMA_LOST_DATA_MSK | + COBALT_SYSSTAT_AUD_IN_LOST_DATA_MSK | + COBALT_SYSSTAT_AUD_OUT_LOST_DATA_MSK); +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-irq.h b/kernel/drivers/media/pci/cobalt/cobalt-irq.h new file mode 100644 index 000000000..5119484a2 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-irq.h @@ -0,0 +1,25 @@ +/* + * cobalt interrupt handling + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +irqreturn_t cobalt_irq_handler(int irq, void *dev_id); +void cobalt_irq_work_handler(struct work_struct *work); +void cobalt_irq_log_status(struct cobalt *cobalt); diff --git a/kernel/drivers/media/pci/cobalt/cobalt-omnitek.c b/kernel/drivers/media/pci/cobalt/cobalt-omnitek.c new file mode 100644 index 000000000..a28a8482c --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-omnitek.c @@ -0,0 +1,341 @@ +/* + * Omnitek Scatter-Gather DMA Controller + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "cobalt-driver.h" +#include "cobalt-omnitek.h" + +/* descriptor */ +#define END_OF_CHAIN (1 << 1) +#define INTERRUPT_ENABLE (1 << 2) +#define WRITE_TO_PCI (1 << 3) +#define READ_FROM_PCI (0 << 3) +#define DESCRIPTOR_FLAG_MSK (END_OF_CHAIN | INTERRUPT_ENABLE | WRITE_TO_PCI) +#define NEXT_ADRS_MSK 0xffffffe0 + +/* control/status register */ +#define ENABLE (1 << 0) +#define START (1 << 1) +#define ABORT (1 << 2) +#define DONE (1 << 4) +#define SG_INTERRUPT (1 << 5) +#define EVENT_INTERRUPT (1 << 6) +#define SCATTER_GATHER_MODE (1 << 8) +#define DISABLE_VIDEO_RESYNC (1 << 9) +#define EVENT_INTERRUPT_ENABLE (1 << 10) +#define DIRECTIONAL_MSK (3 << 16) +#define INPUT_ONLY (0 << 16) +#define OUTPUT_ONLY (1 << 16) +#define BIDIRECTIONAL (2 << 16) +#define DMA_TYPE_MEMORY (0 << 18) +#define DMA_TYPE_FIFO (1 << 18) + +#define BASE (cobalt->bar0) +#define CAPABILITY_HEADER (BASE) +#define CAPABILITY_REGISTER (BASE + 0x04) +#define PCI_64BIT (1 << 8) +#define LOCAL_64BIT (1 << 9) +#define INTERRUPT_STATUS (BASE + 0x08) +#define PCI(c) (BASE + 0x40 + ((c) * 0x40)) +#define SIZE(c) (BASE + 0x58 + ((c) * 0x40)) +#define DESCRIPTOR(c) (BASE + 0x50 + ((c) * 0x40)) +#define CS_REG(c) (BASE + 0x60 + ((c) * 0x40)) +#define BYTES_TRANSFERRED(c) (BASE + 0x64 + ((c) * 0x40)) + + +static char *get_dma_direction(u32 status) +{ + switch (status & DIRECTIONAL_MSK) { + case INPUT_ONLY: return "Input"; + case OUTPUT_ONLY: return "Output"; + case BIDIRECTIONAL: return "Bidirectional"; + } + return ""; +} + +static void show_dma_capability(struct cobalt *cobalt) +{ + u32 header = ioread32(CAPABILITY_HEADER); + u32 capa = ioread32(CAPABILITY_REGISTER); + u32 i; + + cobalt_info("Omnitek DMA capability: ID 0x%02x Version 0x%02x Next 0x%x Size 0x%x\n", + header & 0xff, (header >> 8) & 0xff, + (header >> 16) & 0xffff, (capa >> 24) & 0xff); + + switch ((capa >> 8) & 0x3) { + case 0: + cobalt_info("Omnitek DMA: 32 bits PCIe and Local\n"); + break; + case 1: + cobalt_info("Omnitek DMA: 64 bits PCIe, 32 bits Local\n"); + break; + case 3: + cobalt_info("Omnitek DMA: 64 bits PCIe and Local\n"); + break; + } + + for (i = 0; i < (capa & 0xf); i++) { + u32 status = ioread32(CS_REG(i)); + + cobalt_info("Omnitek DMA channel #%d: %s %s\n", i, + status & DMA_TYPE_FIFO ? "FIFO" : "MEMORY", + get_dma_direction(status)); + } +} + +void omni_sg_dma_start(struct cobalt_stream *s, struct sg_dma_desc_info *desc) +{ + struct cobalt *cobalt = s->cobalt; + + iowrite32((u32)((u64)desc->bus >> 32), DESCRIPTOR(s->dma_channel) + 4); + iowrite32((u32)desc->bus & NEXT_ADRS_MSK, DESCRIPTOR(s->dma_channel)); + iowrite32(ENABLE | SCATTER_GATHER_MODE | START, CS_REG(s->dma_channel)); +} + +bool is_dma_done(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + + if (ioread32(CS_REG(s->dma_channel)) & DONE) + return true; + + return false; +} + +void omni_sg_dma_abort_channel(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + + if (is_dma_done(s) == false) + iowrite32(ABORT, CS_REG(s->dma_channel)); +} + +int omni_sg_dma_init(struct cobalt *cobalt) +{ + u32 capa = ioread32(CAPABILITY_REGISTER); + int i; + + cobalt->first_fifo_channel = 0; + cobalt->dma_channels = capa & 0xf; + if (capa & PCI_64BIT) + cobalt->pci_32_bit = false; + else + cobalt->pci_32_bit = true; + + for (i = 0; i < cobalt->dma_channels; i++) { + u32 status = ioread32(CS_REG(i)); + u32 ctrl = ioread32(CS_REG(i)); + + if (!(ctrl & DONE)) + iowrite32(ABORT, CS_REG(i)); + + if (!(status & DMA_TYPE_FIFO)) + cobalt->first_fifo_channel++; + } + show_dma_capability(cobalt); + return 0; +} + +int descriptor_list_create(struct cobalt *cobalt, + struct scatterlist *scatter_list, bool to_pci, unsigned sglen, + unsigned size, unsigned width, unsigned stride, + struct sg_dma_desc_info *desc) +{ + struct sg_dma_descriptor *d = (struct sg_dma_descriptor *)desc->virt; + dma_addr_t next = desc->bus; + unsigned offset = 0; + unsigned copy_bytes = width; + unsigned copied = 0; + bool first = true; + + /* Must be 4-byte aligned */ + WARN_ON(sg_dma_address(scatter_list) & 3); + WARN_ON(size & 3); + WARN_ON(next & 3); + WARN_ON(stride & 3); + WARN_ON(stride < width); + if (width >= stride) + copy_bytes = stride = size; + + while (size) { + dma_addr_t addr = sg_dma_address(scatter_list) + offset; + unsigned bytes; + + if (addr == 0) + return -EFAULT; + if (cobalt->pci_32_bit) { + WARN_ON((u64)addr >> 32); + if ((u64)addr >> 32) + return -EFAULT; + } + + /* PCIe address */ + d->pci_l = addr & 0xffffffff; + /* If dma_addr_t is 32 bits, then addr >> 32 is actually the + equivalent of addr >> 0 in gcc. So must cast to u64. */ + d->pci_h = (u64)addr >> 32; + + /* Sync to start of streaming frame */ + d->local = 0; + d->reserved0 = 0; + + /* Transfer bytes */ + bytes = min(sg_dma_len(scatter_list) - offset, + copy_bytes - copied); + + if (first) { + if (to_pci) + d->local = 0x11111111; + first = false; + if (sglen == 1) { + /* Make sure there are always at least two + * descriptors */ + d->bytes = (bytes / 2) & ~3; + d->reserved1 = 0; + size -= d->bytes; + copied += d->bytes; + offset += d->bytes; + addr += d->bytes; + next += sizeof(struct sg_dma_descriptor); + d->next_h = (u32)((u64)next >> 32); + d->next_l = (u32)next | + (to_pci ? WRITE_TO_PCI : 0); + bytes -= d->bytes; + d++; + /* PCIe address */ + d->pci_l = addr & 0xffffffff; + /* If dma_addr_t is 32 bits, then addr >> 32 + * is actually the equivalent of addr >> 0 in + * gcc. So must cast to u64. */ + d->pci_h = (u64)addr >> 32; + + /* Sync to start of streaming frame */ + d->local = 0; + d->reserved0 = 0; + } + } + + d->bytes = bytes; + d->reserved1 = 0; + size -= bytes; + copied += bytes; + offset += bytes; + + if (copied == copy_bytes) { + while (copied < stride) { + bytes = min(sg_dma_len(scatter_list) - offset, + stride - copied); + copied += bytes; + offset += bytes; + size -= bytes; + if (sg_dma_len(scatter_list) == offset) { + offset = 0; + scatter_list = sg_next(scatter_list); + } + } + copied = 0; + } else { + offset = 0; + scatter_list = sg_next(scatter_list); + } + + /* Next descriptor + control bits */ + next += sizeof(struct sg_dma_descriptor); + if (size == 0) { + /* Loopback to the first descriptor */ + d->next_h = (u32)((u64)desc->bus >> 32); + d->next_l = (u32)desc->bus | + (to_pci ? WRITE_TO_PCI : 0) | INTERRUPT_ENABLE; + if (!to_pci) + d->local = 0x22222222; + desc->last_desc_virt = d; + } else { + d->next_h = (u32)((u64)next >> 32); + d->next_l = (u32)next | (to_pci ? WRITE_TO_PCI : 0); + } + d++; + } + return 0; +} + +void descriptor_list_chain(struct sg_dma_desc_info *this, + struct sg_dma_desc_info *next) +{ + struct sg_dma_descriptor *d = this->last_desc_virt; + u32 direction = d->next_l & WRITE_TO_PCI; + + if (next == NULL) { + d->next_h = 0; + d->next_l = direction | INTERRUPT_ENABLE | END_OF_CHAIN; + } else { + d->next_h = (u32)((u64)next->bus >> 32); + d->next_l = (u32)next->bus | direction | INTERRUPT_ENABLE; + } +} + +void *descriptor_list_allocate(struct sg_dma_desc_info *desc, size_t bytes) +{ + desc->size = bytes; + desc->virt = dma_alloc_coherent(desc->dev, bytes, + &desc->bus, GFP_KERNEL); + return desc->virt; +} + +void descriptor_list_free(struct sg_dma_desc_info *desc) +{ + if (desc->virt) + dma_free_coherent(desc->dev, desc->size, + desc->virt, desc->bus); + desc->virt = NULL; +} + +void descriptor_list_interrupt_enable(struct sg_dma_desc_info *desc) +{ + struct sg_dma_descriptor *d = desc->last_desc_virt; + + d->next_l |= INTERRUPT_ENABLE; +} + +void descriptor_list_interrupt_disable(struct sg_dma_desc_info *desc) +{ + struct sg_dma_descriptor *d = desc->last_desc_virt; + + d->next_l &= ~INTERRUPT_ENABLE; +} + +void descriptor_list_loopback(struct sg_dma_desc_info *desc) +{ + struct sg_dma_descriptor *d = desc->last_desc_virt; + + d->next_h = (u32)((u64)desc->bus >> 32); + d->next_l = (u32)desc->bus | (d->next_l & DESCRIPTOR_FLAG_MSK); +} + +void descriptor_list_end_of_chain(struct sg_dma_desc_info *desc) +{ + struct sg_dma_descriptor *d = desc->last_desc_virt; + + d->next_l |= END_OF_CHAIN; +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-omnitek.h b/kernel/drivers/media/pci/cobalt/cobalt-omnitek.h new file mode 100644 index 000000000..e5c6d032c --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-omnitek.h @@ -0,0 +1,62 @@ +/* + * Omnitek Scatter-Gather DMA Controller + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COBALT_OMNITEK_H +#define COBALT_OMNITEK_H + +#include +#include "cobalt-driver.h" + +struct sg_dma_descriptor { + u32 pci_l; + u32 pci_h; + + u32 local; + u32 reserved0; + + u32 next_l; + u32 next_h; + + u32 bytes; + u32 reserved1; +}; + +int omni_sg_dma_init(struct cobalt *cobalt); +void omni_sg_dma_abort_channel(struct cobalt_stream *s); +void omni_sg_dma_start(struct cobalt_stream *s, struct sg_dma_desc_info *desc); +bool is_dma_done(struct cobalt_stream *s); + +int descriptor_list_create(struct cobalt *cobalt, + struct scatterlist *scatter_list, bool to_pci, unsigned sglen, + unsigned size, unsigned width, unsigned stride, + struct sg_dma_desc_info *desc); + +void descriptor_list_chain(struct sg_dma_desc_info *this, + struct sg_dma_desc_info *next); +void descriptor_list_loopback(struct sg_dma_desc_info *desc); +void descriptor_list_end_of_chain(struct sg_dma_desc_info *desc); + +void *descriptor_list_allocate(struct sg_dma_desc_info *desc, size_t bytes); +void descriptor_list_free(struct sg_dma_desc_info *desc); + +void descriptor_list_interrupt_enable(struct sg_dma_desc_info *desc); +void descriptor_list_interrupt_disable(struct sg_dma_desc_info *desc); + +#endif diff --git a/kernel/drivers/media/pci/cobalt/cobalt-v4l2.c b/kernel/drivers/media/pci/cobalt/cobalt-v4l2.c new file mode 100644 index 000000000..ff46e4242 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -0,0 +1,1279 @@ +/* + * cobalt V4L2 API + * + * Derived from ivtv-ioctl.c and cx18-fileops.c + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cobalt-alsa.h" +#include "cobalt-cpld.h" +#include "cobalt-driver.h" +#include "cobalt-v4l2.h" +#include "cobalt-irq.h" +#include "cobalt-omnitek.h" + +static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60; + +/* vb2 DMA streaming ops */ + +static int cobalt_queue_setup(struct vb2_queue *q, const void *parg, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + const struct v4l2_format *fmt = parg; + struct cobalt_stream *s = q->drv_priv; + unsigned size = s->stride * s->height; + + if (*num_buffers < 3) + *num_buffers = 3; + if (*num_buffers > NR_BUFS) + *num_buffers = NR_BUFS; + *num_planes = 1; + if (fmt) { + if (fmt->fmt.pix.sizeimage < size) + return -EINVAL; + size = fmt->fmt.pix.sizeimage; + } + sizes[0] = size; + alloc_ctxs[0] = s->cobalt->alloc_ctx; + return 0; +} + +static int cobalt_buf_init(struct vb2_buffer *vb) +{ + struct cobalt_stream *s = vb->vb2_queue->drv_priv; + struct cobalt *cobalt = s->cobalt; + const size_t max_pages_per_line = + (COBALT_MAX_WIDTH * COBALT_MAX_BPP) / PAGE_SIZE + 2; + const size_t bytes = + COBALT_MAX_HEIGHT * max_pages_per_line * 0x20; + const size_t audio_bytes = ((1920 * 4) / PAGE_SIZE + 1) * 0x20; + struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; + struct sg_table *sg_desc = vb2_dma_sg_plane_desc(vb, 0); + unsigned size; + int ret; + + size = s->stride * s->height; + if (vb2_plane_size(vb, 0) < size) { + cobalt_info("data will not fit into plane (%lu < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + if (desc->virt == NULL) { + desc->dev = &cobalt->pci_dev->dev; + descriptor_list_allocate(desc, + s->is_audio ? audio_bytes : bytes); + if (desc->virt == NULL) + return -ENOMEM; + } + ret = descriptor_list_create(cobalt, sg_desc->sgl, + !s->is_output, sg_desc->nents, size, + s->width * s->bpp, s->stride, desc); + if (ret) + descriptor_list_free(desc); + return ret; +} + +static void cobalt_buf_cleanup(struct vb2_buffer *vb) +{ + struct cobalt_stream *s = vb->vb2_queue->drv_priv; + struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; + + descriptor_list_free(desc); +} + +static int cobalt_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct cobalt_stream *s = vb->vb2_queue->drv_priv; + + vb2_set_plane_payload(vb, 0, s->stride * s->height); + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static void chain_all_buffers(struct cobalt_stream *s) +{ + struct sg_dma_desc_info *desc[NR_BUFS]; + struct cobalt_buffer *cb; + struct list_head *p; + int i = 0; + + list_for_each(p, &s->bufs) { + cb = list_entry(p, struct cobalt_buffer, list); + desc[i] = &s->dma_desc_info[cb->vb.vb2_buf.index]; + if (i > 0) + descriptor_list_chain(desc[i-1], desc[i]); + i++; + } +} + +static void cobalt_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *q = vb->vb2_queue; + struct cobalt_stream *s = q->drv_priv; + struct cobalt_buffer *cb = to_cobalt_buffer(vbuf); + struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; + unsigned long flags; + + /* Prepare new buffer */ + descriptor_list_loopback(desc); + descriptor_list_interrupt_disable(desc); + + spin_lock_irqsave(&s->irqlock, flags); + list_add_tail(&cb->list, &s->bufs); + chain_all_buffers(s); + spin_unlock_irqrestore(&s->irqlock, flags); +} + +static void cobalt_enable_output(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + struct v4l2_bt_timings *bt = &s->timings.bt; + struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = + COBALT_TX_BASE(cobalt); + unsigned fmt = s->pixfmt != V4L2_PIX_FMT_BGR32 ? + M00514_CONTROL_BITMAP_FORMAT_16_BPP_MSK : 0; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + if (!cobalt_cpld_set_freq(cobalt, bt->pixelclock)) { + cobalt_err("pixelclock out of range\n"); + return; + } + + sd_fmt.format.colorspace = s->colorspace; + sd_fmt.format.xfer_func = s->xfer_func; + sd_fmt.format.ycbcr_enc = s->ycbcr_enc; + sd_fmt.format.quantization = s->quantization; + sd_fmt.format.width = bt->width; + sd_fmt.format.height = bt->height; + + /* Set up FDMA packer */ + switch (s->pixfmt) { + case V4L2_PIX_FMT_YUYV: + sd_fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + case V4L2_PIX_FMT_BGR32: + sd_fmt.format.code = MEDIA_BUS_FMT_RGB888_1X24; + break; + } + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); + + iowrite32(0, &vo->control); + /* 1080p60 */ + iowrite32(bt->hsync, &vo->sync_generator_h_sync_length); + iowrite32(bt->hbackporch, &vo->sync_generator_h_backporch_length); + iowrite32(bt->width, &vo->sync_generator_h_active_length); + iowrite32(bt->hfrontporch, &vo->sync_generator_h_frontporch_length); + iowrite32(bt->vsync, &vo->sync_generator_v_sync_length); + iowrite32(bt->vbackporch, &vo->sync_generator_v_backporch_length); + iowrite32(bt->height, &vo->sync_generator_v_active_length); + iowrite32(bt->vfrontporch, &vo->sync_generator_v_frontporch_length); + iowrite32(0x9900c1, &vo->error_color); + + iowrite32(M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_MSK | fmt, + &vo->control); + iowrite32(M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK | fmt, &vo->control); + iowrite32(M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_MSK | + M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_MSK | + fmt, &vo->control); +} + +static void cobalt_enable_input(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + int ch = (int)s->video_channel; + struct m00235_fdma_packer_regmap __iomem *packer; + struct v4l2_subdev_format sd_fmt_yuyv = { + .pad = s->pad_source, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format.code = MEDIA_BUS_FMT_YUYV8_1X16, + }; + struct v4l2_subdev_format sd_fmt_rgb = { + .pad = s->pad_source, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format.code = MEDIA_BUS_FMT_RGB888_1X24, + }; + + cobalt_dbg(1, "video_channel %d (%s, %s)\n", + s->video_channel, + s->input == 0 ? "hdmi" : "generator", + "YUYV"); + + packer = COBALT_CVI_PACKER(cobalt, ch); + + /* Set up FDMA packer */ + switch (s->pixfmt) { + case V4L2_PIX_FMT_YUYV: + iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | + (1 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), + &packer->control); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + &sd_fmt_yuyv); + break; + case V4L2_PIX_FMT_RGB24: + iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | + (2 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), + &packer->control); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + &sd_fmt_rgb); + break; + case V4L2_PIX_FMT_BGR32: + iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | + M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK | + (3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), + &packer->control); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + &sd_fmt_rgb); + break; + } +} + +static void cobalt_dma_start_streaming(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + int rx = s->video_channel; + struct m00460_evcnt_regmap __iomem *evcnt = + COBALT_CVI_EVCNT(cobalt, rx); + struct cobalt_buffer *cb; + unsigned long flags; + + spin_lock_irqsave(&s->irqlock, flags); + if (!s->is_output) { + iowrite32(M00460_CONTROL_BITMAP_CLEAR_MSK, &evcnt->control); + iowrite32(M00460_CONTROL_BITMAP_ENABLE_MSK, &evcnt->control); + } else { + struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = + COBALT_TX_BASE(cobalt); + u32 ctrl = ioread32(&vo->control); + + ctrl &= ~(M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK | + M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK); + iowrite32(ctrl | M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK, + &vo->control); + iowrite32(ctrl | M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK, + &vo->control); + } + cb = list_first_entry(&s->bufs, struct cobalt_buffer, list); + omni_sg_dma_start(s, &s->dma_desc_info[cb->vb.vb2_buf.index]); + spin_unlock_irqrestore(&s->irqlock, flags); +} + +static int cobalt_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct cobalt_stream *s = q->drv_priv; + struct cobalt *cobalt = s->cobalt; + struct m00233_video_measure_regmap __iomem *vmr; + struct m00473_freewheel_regmap __iomem *fw; + struct m00479_clk_loss_detector_regmap __iomem *clkloss; + int rx = s->video_channel; + struct m00389_cvi_regmap __iomem *cvi = COBALT_CVI(cobalt, rx); + struct m00460_evcnt_regmap __iomem *evcnt = COBALT_CVI_EVCNT(cobalt, rx); + struct v4l2_bt_timings *bt = &s->timings.bt; + u64 tot_size; + u32 clk_freq; + + if (s->is_audio) + goto done; + if (s->is_output) { + s->unstable_frame = false; + cobalt_enable_output(s); + goto done; + } + + cobalt_enable_input(s); + + fw = COBALT_CVI_FREEWHEEL(cobalt, rx); + vmr = COBALT_CVI_VMR(cobalt, rx); + clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); + + iowrite32(M00460_CONTROL_BITMAP_CLEAR_MSK, &evcnt->control); + iowrite32(M00460_CONTROL_BITMAP_ENABLE_MSK, &evcnt->control); + iowrite32(bt->width, &cvi->frame_width); + iowrite32(bt->height, &cvi->frame_height); + tot_size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + iowrite32(div_u64((u64)V4L2_DV_BT_FRAME_WIDTH(bt) * COBALT_CLK * 4, + bt->pixelclock), &vmr->hsync_timeout_val); + iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); + clk_freq = ioread32(&fw->clk_freq); + iowrite32(clk_freq / 1000000, &clkloss->ref_clk_cnt_val); + /* The lower bound for the clock frequency is 0.5% lower as is + * allowed by the spec */ + iowrite32(div_u64(bt->pixelclock * 995, 1000000000), + &clkloss->test_clk_cnt_val); + /* will be enabled after the first frame has been received */ + iowrite32(bt->width * bt->height, &fw->active_length); + iowrite32(div_u64((u64)clk_freq * tot_size, bt->pixelclock), + &fw->total_length); + iowrite32(M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_MSK | + M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_MSK, + &vmr->irq_triggers); + iowrite32(0, &cvi->control); + iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); + + iowrite32(0xff, &fw->output_color); + iowrite32(M00479_CTRL_BITMAP_ENABLE_MSK, &clkloss->ctrl); + iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK | + M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK, &fw->ctrl); + s->unstable_frame = true; + s->enable_freewheel = false; + s->enable_cvi = false; + s->skip_first_frames = 0; + +done: + s->sequence = 0; + cobalt_dma_start_streaming(s); + return 0; +} + +static void cobalt_dma_stop_streaming(struct cobalt_stream *s) +{ + struct cobalt *cobalt = s->cobalt; + struct sg_dma_desc_info *desc; + struct cobalt_buffer *cb; + struct list_head *p; + unsigned long flags; + int timeout_msec = 100; + int rx = s->video_channel; + struct m00460_evcnt_regmap __iomem *evcnt = + COBALT_CVI_EVCNT(cobalt, rx); + + if (!s->is_output) { + iowrite32(0, &evcnt->control); + } else if (!s->is_audio) { + struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = + COBALT_TX_BASE(cobalt); + + iowrite32(M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK, &vo->control); + iowrite32(0, &vo->control); + } + + /* Try to stop the DMA engine gracefully */ + spin_lock_irqsave(&s->irqlock, flags); + list_for_each(p, &s->bufs) { + cb = list_entry(p, struct cobalt_buffer, list); + desc = &s->dma_desc_info[cb->vb.vb2_buf.index]; + /* Stop DMA after this descriptor chain */ + descriptor_list_end_of_chain(desc); + } + spin_unlock_irqrestore(&s->irqlock, flags); + + /* Wait 100 milisecond for DMA to finish, abort on timeout. */ + if (!wait_event_timeout(s->q.done_wq, is_dma_done(s), + msecs_to_jiffies(timeout_msec))) { + omni_sg_dma_abort_channel(s); + pr_warn("aborted\n"); + } + cobalt_write_bar0(cobalt, DMA_INTERRUPT_STATUS_REG, + 1 << s->dma_channel); +} + +static void cobalt_stop_streaming(struct vb2_queue *q) +{ + struct cobalt_stream *s = q->drv_priv; + struct cobalt *cobalt = s->cobalt; + int rx = s->video_channel; + struct m00233_video_measure_regmap __iomem *vmr; + struct m00473_freewheel_regmap __iomem *fw; + struct m00479_clk_loss_detector_regmap __iomem *clkloss; + struct cobalt_buffer *cb; + struct list_head *p, *safe; + unsigned long flags; + + cobalt_dma_stop_streaming(s); + + /* Return all buffers to user space */ + spin_lock_irqsave(&s->irqlock, flags); + list_for_each_safe(p, safe, &s->bufs) { + cb = list_entry(p, struct cobalt_buffer, list); + list_del(&cb->list); + vb2_buffer_done(&cb->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&s->irqlock, flags); + + if (s->is_audio || s->is_output) + return; + + fw = COBALT_CVI_FREEWHEEL(cobalt, rx); + vmr = COBALT_CVI_VMR(cobalt, rx); + clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); + iowrite32(0, &vmr->control); + iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); + iowrite32(0, &fw->ctrl); + iowrite32(0, &clkloss->ctrl); +} + +static const struct vb2_ops cobalt_qops = { + .queue_setup = cobalt_queue_setup, + .buf_init = cobalt_buf_init, + .buf_cleanup = cobalt_buf_cleanup, + .buf_prepare = cobalt_buf_prepare, + .buf_queue = cobalt_buf_queue, + .start_streaming = cobalt_start_streaming, + .stop_streaming = cobalt_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* V4L2 ioctls */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cobalt_cobaltc(struct cobalt *cobalt, unsigned int cmd, void *arg) +{ + struct v4l2_dbg_register *regs = arg; + void __iomem *adrs = cobalt->bar1 + regs->reg; + + cobalt_info("cobalt_cobaltc: adrs = %p\n", adrs); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + regs->size = 4; + if (cmd == VIDIOC_DBG_S_REGISTER) + iowrite32(regs->val, adrs); + else + regs->val = ioread32(adrs); + return 0; +} + +static int cobalt_g_register(struct file *file, void *priv_fh, + struct v4l2_dbg_register *reg) +{ + struct cobalt_stream *s = video_drvdata(file); + struct cobalt *cobalt = s->cobalt; + + return cobalt_cobaltc(cobalt, VIDIOC_DBG_G_REGISTER, reg); +} + +static int cobalt_s_register(struct file *file, void *priv_fh, + const struct v4l2_dbg_register *reg) +{ + struct cobalt_stream *s = video_drvdata(file); + struct cobalt *cobalt = s->cobalt; + + return cobalt_cobaltc(cobalt, VIDIOC_DBG_S_REGISTER, + (struct v4l2_dbg_register *)reg); +} +#endif + +static int cobalt_querycap(struct file *file, void *priv_fh, + struct v4l2_capability *vcap) +{ + struct cobalt_stream *s = video_drvdata(file); + struct cobalt *cobalt = s->cobalt; + + strlcpy(vcap->driver, "cobalt", sizeof(vcap->driver)); + strlcpy(vcap->card, "cobalt", sizeof(vcap->card)); + snprintf(vcap->bus_info, sizeof(vcap->bus_info), + "PCIe:%s", pci_name(cobalt->pci_dev)); + vcap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (s->is_output) + vcap->device_caps |= V4L2_CAP_VIDEO_OUTPUT; + else + vcap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_VIDEO_CAPTURE; + if (cobalt->have_hsma_tx) + vcap->capabilities |= V4L2_CAP_VIDEO_OUTPUT; + return 0; +} + +static void cobalt_video_input_status_show(struct cobalt_stream *s) +{ + struct m00389_cvi_regmap __iomem *cvi; + struct m00233_video_measure_regmap __iomem *vmr; + struct m00473_freewheel_regmap __iomem *fw; + struct m00479_clk_loss_detector_regmap __iomem *clkloss; + struct m00235_fdma_packer_regmap __iomem *packer; + int rx = s->video_channel; + struct cobalt *cobalt = s->cobalt; + u32 cvi_ctrl, cvi_stat; + u32 vmr_ctrl, vmr_stat; + + cvi = COBALT_CVI(cobalt, rx); + vmr = COBALT_CVI_VMR(cobalt, rx); + fw = COBALT_CVI_FREEWHEEL(cobalt, rx); + clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); + packer = COBALT_CVI_PACKER(cobalt, rx); + cvi_ctrl = ioread32(&cvi->control); + cvi_stat = ioread32(&cvi->status); + vmr_ctrl = ioread32(&vmr->control); + vmr_stat = ioread32(&vmr->control); + cobalt_info("rx%d: cvi resolution: %dx%d\n", rx, + ioread32(&cvi->frame_width), ioread32(&cvi->frame_height)); + cobalt_info("rx%d: cvi control: %s%s%s\n", rx, + (cvi_ctrl & M00389_CONTROL_BITMAP_ENABLE_MSK) ? + "enable " : "disable ", + (cvi_ctrl & M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ? + "HSync- " : "HSync+ ", + (cvi_ctrl & M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ? + "VSync- " : "VSync+ "); + cobalt_info("rx%d: cvi status: %s%s\n", rx, + (cvi_stat & M00389_STATUS_BITMAP_LOCK_MSK) ? + "lock " : "no-lock ", + (cvi_stat & M00389_STATUS_BITMAP_ERROR_MSK) ? + "error " : "no-error "); + + cobalt_info("rx%d: Measurements: %s%s%s%s%s%s%s\n", rx, + (vmr_ctrl & M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ? + "HSync- " : "HSync+ ", + (vmr_ctrl & M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ? + "VSync- " : "VSync+ ", + (vmr_ctrl & M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK) ? + "enabled " : "disabled ", + (vmr_ctrl & M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK) ? + "irq-enabled " : "irq-disabled ", + (vmr_ctrl & M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_MSK) ? + "update-on-hsync " : "", + (vmr_stat & M00233_STATUS_BITMAP_HSYNC_TIMEOUT_MSK) ? + "hsync-timeout " : "", + (vmr_stat & M00233_STATUS_BITMAP_INIT_DONE_MSK) ? + "init-done" : ""); + cobalt_info("rx%d: irq_status: 0x%02x irq_triggers: 0x%02x\n", rx, + ioread32(&vmr->irq_status) & 0xff, + ioread32(&vmr->irq_triggers) & 0xff); + cobalt_info("rx%d: vsync: %d\n", rx, ioread32(&vmr->vsync_time)); + cobalt_info("rx%d: vbp: %d\n", rx, ioread32(&vmr->vback_porch)); + cobalt_info("rx%d: vact: %d\n", rx, ioread32(&vmr->vactive_area)); + cobalt_info("rx%d: vfb: %d\n", rx, ioread32(&vmr->vfront_porch)); + cobalt_info("rx%d: hsync: %d\n", rx, ioread32(&vmr->hsync_time)); + cobalt_info("rx%d: hbp: %d\n", rx, ioread32(&vmr->hback_porch)); + cobalt_info("rx%d: hact: %d\n", rx, ioread32(&vmr->hactive_area)); + cobalt_info("rx%d: hfb: %d\n", rx, ioread32(&vmr->hfront_porch)); + cobalt_info("rx%d: Freewheeling: %s%s%s\n", rx, + (ioread32(&fw->ctrl) & M00473_CTRL_BITMAP_ENABLE_MSK) ? + "enabled " : "disabled ", + (ioread32(&fw->ctrl) & M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK) ? + "forced " : "", + (ioread32(&fw->status) & M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK) ? + "freewheeling " : "video-passthrough "); + iowrite32(0xff, &vmr->irq_status); + cobalt_info("rx%d: Clock Loss Detection: %s%s\n", rx, + (ioread32(&clkloss->ctrl) & M00479_CTRL_BITMAP_ENABLE_MSK) ? + "enabled " : "disabled ", + (ioread32(&clkloss->status) & M00479_STATUS_BITMAP_CLOCK_MISSING_MSK) ? + "clock-missing " : "found-clock "); + cobalt_info("rx%d: Packer: %x\n", rx, ioread32(&packer->control)); +} + +static int cobalt_log_status(struct file *file, void *priv_fh) +{ + struct cobalt_stream *s = video_drvdata(file); + struct cobalt *cobalt = s->cobalt; + struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = + COBALT_TX_BASE(cobalt); + u8 stat; + + cobalt_info("%s", cobalt->hdl_info); + cobalt_info("sysctrl: %08x, sysstat: %08x\n", + cobalt_g_sysctrl(cobalt), + cobalt_g_sysstat(cobalt)); + cobalt_info("dma channel: %d, video channel: %d\n", + s->dma_channel, s->video_channel); + cobalt_pcie_status_show(cobalt); + cobalt_cpld_status(cobalt); + cobalt_irq_log_status(cobalt); + v4l2_subdev_call(s->sd, core, log_status); + if (!s->is_output) { + cobalt_video_input_status_show(s); + return 0; + } + + stat = ioread32(&vo->rd_status); + + cobalt_info("tx: status: %s%s\n", + (stat & M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_MSK) ? + "no_data " : "", + (stat & M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_MSK) ? + "ready_buffer_full " : ""); + cobalt_info("tx: evcnt: %d\n", ioread32(&vo->rd_evcnt_count)); + return 0; +} + +static int cobalt_enum_dv_timings(struct file *file, void *priv_fh, + struct v4l2_enum_dv_timings *timings) +{ + struct cobalt_stream *s = video_drvdata(file); + + if (s->input == 1) { + if (timings->index) + return -EINVAL; + memset(timings->reserved, 0, sizeof(timings->reserved)); + timings->timings = cea1080p60; + return 0; + } + timings->pad = 0; + return v4l2_subdev_call(s->sd, + pad, enum_dv_timings, timings); +} + +static int cobalt_s_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct cobalt_stream *s = video_drvdata(file); + int err; + + if (s->input == 1) { + *timings = cea1080p60; + return 0; + } + + if (v4l2_match_dv_timings(timings, &s->timings, 0)) + return 0; + + if (vb2_is_busy(&s->q)) + return -EBUSY; + + err = v4l2_subdev_call(s->sd, + video, s_dv_timings, timings); + if (!err) { + s->timings = *timings; + s->width = timings->bt.width; + s->height = timings->bt.height; + s->stride = timings->bt.width * s->bpp; + } + return err; +} + +static int cobalt_g_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct cobalt_stream *s = video_drvdata(file); + + if (s->input == 1) { + *timings = cea1080p60; + return 0; + } + return v4l2_subdev_call(s->sd, + video, g_dv_timings, timings); +} + +static int cobalt_query_dv_timings(struct file *file, void *priv_fh, + struct v4l2_dv_timings *timings) +{ + struct cobalt_stream *s = video_drvdata(file); + + if (s->input == 1) { + *timings = cea1080p60; + return 0; + } + return v4l2_subdev_call(s->sd, + video, query_dv_timings, timings); +} + +static int cobalt_dv_timings_cap(struct file *file, void *priv_fh, + struct v4l2_dv_timings_cap *cap) +{ + struct cobalt_stream *s = video_drvdata(file); + + cap->pad = 0; + return v4l2_subdev_call(s->sd, + pad, dv_timings_cap, cap); +} + +static int cobalt_enum_fmt_vid_cap(struct file *file, void *priv_fh, + struct v4l2_fmtdesc *f) +{ + switch (f->index) { + case 0: + strlcpy(f->description, "YUV 4:2:2", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + case 1: + strlcpy(f->description, "RGB24", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_RGB24; + break; + case 2: + strlcpy(f->description, "RGB32", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_BGR32; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cobalt_g_fmt_vid_cap(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct cobalt_stream *s = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev_format sd_fmt; + + pix->width = s->width; + pix->height = s->height; + pix->bytesperline = s->stride; + pix->field = V4L2_FIELD_NONE; + + if (s->input == 1) { + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + sd_fmt.pad = s->pad_source; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt); + v4l2_fill_pix_format(pix, &sd_fmt.format); + } + + pix->pixelformat = s->pixfmt; + pix->sizeimage = pix->bytesperline * pix->height; + + return 0; +} + +static int cobalt_try_fmt_vid_cap(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct cobalt_stream *s = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev_format sd_fmt; + + /* Check for min (QCIF) and max (Full HD) size */ + if ((pix->width < 176) || (pix->height < 144)) { + pix->width = 176; + pix->height = 144; + } + + if ((pix->width > 1920) || (pix->height > 1080)) { + pix->width = 1920; + pix->height = 1080; + } + + /* Make width multiple of 4 */ + pix->width &= ~0x3; + + /* Make height multiple of 2 */ + pix->height &= ~0x1; + + if (s->input == 1) { + /* Generator => fixed format only */ + pix->width = 1920; + pix->height = 1080; + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + sd_fmt.pad = s->pad_source; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt); + v4l2_fill_pix_format(pix, &sd_fmt.format); + } + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + default: + pix->bytesperline = max(pix->bytesperline & ~0x3, + pix->width * COBALT_BYTES_PER_PIXEL_YUYV); + pix->pixelformat = V4L2_PIX_FMT_YUYV; + break; + case V4L2_PIX_FMT_RGB24: + pix->bytesperline = max(pix->bytesperline & ~0x3, + pix->width * COBALT_BYTES_PER_PIXEL_RGB24); + break; + case V4L2_PIX_FMT_BGR32: + pix->bytesperline = max(pix->bytesperline & ~0x3, + pix->width * COBALT_BYTES_PER_PIXEL_RGB32); + break; + } + + pix->sizeimage = pix->bytesperline * pix->height; + pix->field = V4L2_FIELD_NONE; + pix->priv = 0; + + return 0; +} + +static int cobalt_s_fmt_vid_cap(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct cobalt_stream *s = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (vb2_is_busy(&s->q)) + return -EBUSY; + + if (cobalt_try_fmt_vid_cap(file, priv_fh, f)) + return -EINVAL; + + s->width = pix->width; + s->height = pix->height; + s->stride = pix->bytesperline; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; + break; + case V4L2_PIX_FMT_RGB24: + s->bpp = COBALT_BYTES_PER_PIXEL_RGB24; + break; + case V4L2_PIX_FMT_BGR32: + s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; + break; + default: + return -EINVAL; + } + s->pixfmt = pix->pixelformat; + cobalt_enable_input(s); + + return 0; +} + +static int cobalt_try_fmt_vid_out(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + /* Check for min (QCIF) and max (Full HD) size */ + if ((pix->width < 176) || (pix->height < 144)) { + pix->width = 176; + pix->height = 144; + } + + if ((pix->width > 1920) || (pix->height > 1080)) { + pix->width = 1920; + pix->height = 1080; + } + + /* Make width multiple of 4 */ + pix->width &= ~0x3; + + /* Make height multiple of 2 */ + pix->height &= ~0x1; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + default: + pix->bytesperline = max(pix->bytesperline & ~0x3, + pix->width * COBALT_BYTES_PER_PIXEL_YUYV); + pix->pixelformat = V4L2_PIX_FMT_YUYV; + break; + case V4L2_PIX_FMT_BGR32: + pix->bytesperline = max(pix->bytesperline & ~0x3, + pix->width * COBALT_BYTES_PER_PIXEL_RGB32); + break; + } + + pix->sizeimage = pix->bytesperline * pix->height; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int cobalt_g_fmt_vid_out(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct cobalt_stream *s = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = s->width; + pix->height = s->height; + pix->bytesperline = s->stride; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = s->pixfmt; + pix->colorspace = s->colorspace; + pix->xfer_func = s->xfer_func; + pix->ycbcr_enc = s->ycbcr_enc; + pix->quantization = s->quantization; + pix->sizeimage = pix->bytesperline * pix->height; + + return 0; +} + +static int cobalt_enum_fmt_vid_out(struct file *file, void *priv_fh, + struct v4l2_fmtdesc *f) +{ + switch (f->index) { + case 0: + strlcpy(f->description, "YUV 4:2:2", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + case 1: + strlcpy(f->description, "RGB32", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_BGR32; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cobalt_s_fmt_vid_out(struct file *file, void *priv_fh, + struct v4l2_format *f) +{ + struct cobalt_stream *s = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev_format sd_fmt = { 0 }; + u32 code; + + if (cobalt_try_fmt_vid_out(file, priv_fh, f)) + return -EINVAL; + + if (vb2_is_busy(&s->q) && (pix->pixelformat != s->pixfmt || + pix->width != s->width || pix->height != s->height || + pix->bytesperline != s->stride)) + return -EBUSY; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; + code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + case V4L2_PIX_FMT_BGR32: + s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; + code = MEDIA_BUS_FMT_RGB888_1X24; + break; + default: + return -EINVAL; + } + s->width = pix->width; + s->height = pix->height; + s->stride = pix->bytesperline; + s->pixfmt = pix->pixelformat; + s->colorspace = pix->colorspace; + s->xfer_func = pix->xfer_func; + s->ycbcr_enc = pix->ycbcr_enc; + s->quantization = pix->quantization; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + v4l2_fill_mbus_format(&sd_fmt.format, pix, code); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); + return 0; +} + +static int cobalt_enum_input(struct file *file, void *priv_fh, + struct v4l2_input *inp) +{ + struct cobalt_stream *s = video_drvdata(file); + + if (inp->index > 1) + return -EINVAL; + if (inp->index == 0) + snprintf(inp->name, sizeof(inp->name), + "HDMI-%d", s->video_channel); + else + snprintf(inp->name, sizeof(inp->name), + "Generator-%d", s->video_channel); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; + if (inp->index == 1) + return 0; + return v4l2_subdev_call(s->sd, + video, g_input_status, &inp->status); +} + +static int cobalt_g_input(struct file *file, void *priv_fh, unsigned int *i) +{ + struct cobalt_stream *s = video_drvdata(file); + + *i = s->input; + return 0; +} + +static int cobalt_s_input(struct file *file, void *priv_fh, unsigned int i) +{ + struct cobalt_stream *s = video_drvdata(file); + + if (i >= 2) + return -EINVAL; + if (vb2_is_busy(&s->q)) + return -EBUSY; + s->input = i; + + cobalt_enable_input(s); + + if (s->input == 1) /* Test Pattern Generator */ + return 0; + + return v4l2_subdev_call(s->sd, video, s_routing, + ADV76XX_PAD_HDMI_PORT_A, 0, 0); +} + +static int cobalt_enum_output(struct file *file, void *priv_fh, + struct v4l2_output *out) +{ + if (out->index) + return -EINVAL; + snprintf(out->name, sizeof(out->name), "HDMI-%d", out->index); + out->type = V4L2_OUTPUT_TYPE_ANALOG; + out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; + return 0; +} + +static int cobalt_g_output(struct file *file, void *priv_fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cobalt_s_output(struct file *file, void *priv_fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int cobalt_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct cobalt_stream *s = video_drvdata(file); + u32 pad = edid->pad; + int ret; + + if (edid->pad >= (s->is_output ? 1 : 2)) + return -EINVAL; + edid->pad = 0; + ret = v4l2_subdev_call(s->sd, pad, get_edid, edid); + edid->pad = pad; + return ret; +} + +static int cobalt_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct cobalt_stream *s = video_drvdata(file); + u32 pad = edid->pad; + int ret; + + if (edid->pad >= 2) + return -EINVAL; + edid->pad = 0; + ret = v4l2_subdev_call(s->sd, pad, set_edid, edid); + edid->pad = pad; + return ret; +} + +static int cobalt_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int cobalt_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 60; + a->parm.capture.readbuffers = 3; + return 0; +} + +static const struct v4l2_ioctl_ops cobalt_ioctl_ops = { + .vidioc_querycap = cobalt_querycap, + .vidioc_g_parm = cobalt_g_parm, + .vidioc_log_status = cobalt_log_status, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = cobalt_enum_input, + .vidioc_g_input = cobalt_g_input, + .vidioc_s_input = cobalt_s_input, + .vidioc_enum_fmt_vid_cap = cobalt_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cobalt_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cobalt_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cobalt_try_fmt_vid_cap, + .vidioc_enum_output = cobalt_enum_output, + .vidioc_g_output = cobalt_g_output, + .vidioc_s_output = cobalt_s_output, + .vidioc_enum_fmt_vid_out = cobalt_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = cobalt_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = cobalt_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = cobalt_try_fmt_vid_out, + .vidioc_s_dv_timings = cobalt_s_dv_timings, + .vidioc_g_dv_timings = cobalt_g_dv_timings, + .vidioc_query_dv_timings = cobalt_query_dv_timings, + .vidioc_enum_dv_timings = cobalt_enum_dv_timings, + .vidioc_dv_timings_cap = cobalt_dv_timings_cap, + .vidioc_g_edid = cobalt_g_edid, + .vidioc_s_edid = cobalt_s_edid, + .vidioc_subscribe_event = cobalt_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .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, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cobalt_g_register, + .vidioc_s_register = cobalt_s_register, +#endif +}; + +static const struct v4l2_ioctl_ops cobalt_ioctl_empty_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cobalt_g_register, + .vidioc_s_register = cobalt_s_register, +#endif +}; + +/* Register device nodes */ + +static const struct v4l2_file_operations cobalt_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .unlocked_ioctl = video_ioctl2, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +static const struct v4l2_file_operations cobalt_out_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .unlocked_ioctl = video_ioctl2, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .write = vb2_fop_write, +}; + +static const struct v4l2_file_operations cobalt_empty_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .unlocked_ioctl = video_ioctl2, + .release = v4l2_fh_release, +}; + +static int cobalt_node_register(struct cobalt *cobalt, int node) +{ + static const struct v4l2_dv_timings dv1080p60 = + V4L2_DV_BT_CEA_1920X1080P60; + struct cobalt_stream *s = cobalt->streams + node; + struct video_device *vdev = &s->vdev; + struct vb2_queue *q = &s->q; + int ret; + + mutex_init(&s->lock); + spin_lock_init(&s->irqlock); + + snprintf(vdev->name, sizeof(vdev->name), + "%s-%d", cobalt->v4l2_dev.name, node); + s->width = 1920; + /* Audio frames are just 4 lines of 1920 bytes */ + s->height = s->is_audio ? 4 : 1080; + + if (s->is_audio) { + s->bpp = 1; + s->pixfmt = V4L2_PIX_FMT_GREY; + } else if (s->is_output) { + s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; + s->pixfmt = V4L2_PIX_FMT_BGR32; + } else { + s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; + s->pixfmt = V4L2_PIX_FMT_YUYV; + } + s->colorspace = V4L2_COLORSPACE_SRGB; + s->stride = s->width * s->bpp; + + if (!s->is_audio) { + if (s->is_dummy) + cobalt_warn("Setting up dummy video node %d\n", node); + vdev->v4l2_dev = &cobalt->v4l2_dev; + if (s->is_dummy) + vdev->fops = &cobalt_empty_fops; + else + vdev->fops = s->is_output ? &cobalt_out_fops : + &cobalt_fops; + vdev->release = video_device_release_empty; + vdev->vfl_dir = s->is_output ? VFL_DIR_TX : VFL_DIR_RX; + vdev->lock = &s->lock; + if (s->sd) + vdev->ctrl_handler = s->sd->ctrl_handler; + s->timings = dv1080p60; + v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings); + if (!s->is_output && s->sd) + cobalt_enable_input(s); + vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops : + &cobalt_ioctl_ops; + } + + INIT_LIST_HEAD(&s->bufs); + q->type = s->is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->io_modes |= s->is_output ? VB2_WRITE : VB2_READ; + q->drv_priv = s; + q->buf_struct_size = sizeof(struct cobalt_buffer); + q->ops = &cobalt_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &s->lock; + vdev->queue = q; + + video_set_drvdata(vdev, s); + ret = vb2_queue_init(q); + if (!s->is_audio && ret == 0) + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + else if (!s->is_dummy) + ret = cobalt_alsa_init(s); + + if (ret < 0) { + if (!s->is_audio) + cobalt_err("couldn't register v4l2 device node %d\n", + node); + return ret; + } + cobalt_info("registered node %d\n", node); + return 0; +} + +/* Initialize v4l2 variables and register v4l2 devices */ +int cobalt_nodes_register(struct cobalt *cobalt) +{ + int node, ret; + + /* Setup V4L2 Devices */ + for (node = 0; node < COBALT_NUM_STREAMS; node++) { + ret = cobalt_node_register(cobalt, node); + if (ret) + return ret; + } + return 0; +} + +/* Unregister v4l2 devices */ +void cobalt_nodes_unregister(struct cobalt *cobalt) +{ + int node; + + /* Teardown all streams */ + for (node = 0; node < COBALT_NUM_STREAMS; node++) { + struct cobalt_stream *s = cobalt->streams + node; + struct video_device *vdev = &s->vdev; + + if (!s->is_audio) + video_unregister_device(vdev); + else if (!s->is_dummy) + cobalt_alsa_exit(s); + } +} diff --git a/kernel/drivers/media/pci/cobalt/cobalt-v4l2.h b/kernel/drivers/media/pci/cobalt/cobalt-v4l2.h new file mode 100644 index 000000000..62be553cd --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/cobalt-v4l2.h @@ -0,0 +1,22 @@ +/* + * cobalt V4L2 API + * + * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +int cobalt_nodes_register(struct cobalt *cobalt); +void cobalt_nodes_unregister(struct cobalt *cobalt); diff --git a/kernel/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h new file mode 100644 index 000000000..9bc9ef1fd --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h @@ -0,0 +1,115 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_H +#define M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00233_video_measure_regmap { + uint32_t irq_status; /* Reg 0x0000 */ + /* The vertical counter starts on rising edge of vsync */ + uint32_t vsync_time; /* Reg 0x0004 */ + uint32_t vback_porch; /* Reg 0x0008 */ + uint32_t vactive_area; /* Reg 0x000c */ + uint32_t vfront_porch; /* Reg 0x0010 */ + /* The horizontal counter starts on rising edge of hsync. */ + uint32_t hsync_time; /* Reg 0x0014 */ + uint32_t hback_porch; /* Reg 0x0018 */ + uint32_t hactive_area; /* Reg 0x001c */ + uint32_t hfront_porch; /* Reg 0x0020 */ + uint32_t control; /* Reg 0x0024, Default=0x0 */ + uint32_t irq_triggers; /* Reg 0x0028, Default=0xff */ + /* Value is given in number of register bus clock periods between */ + /* falling and rising edge of hsync. Must be non-zero. */ + uint32_t hsync_timeout_val; /* Reg 0x002c, Default=0x1fff */ + uint32_t status; /* Reg 0x0030 */ +}; + +#define M00233_VIDEO_MEASURE_REG_IRQ_STATUS_OFST 0 +#define M00233_VIDEO_MEASURE_REG_VSYNC_TIME_OFST 4 +#define M00233_VIDEO_MEASURE_REG_VBACK_PORCH_OFST 8 +#define M00233_VIDEO_MEASURE_REG_VACTIVE_AREA_OFST 12 +#define M00233_VIDEO_MEASURE_REG_VFRONT_PORCH_OFST 16 +#define M00233_VIDEO_MEASURE_REG_HSYNC_TIME_OFST 20 +#define M00233_VIDEO_MEASURE_REG_HBACK_PORCH_OFST 24 +#define M00233_VIDEO_MEASURE_REG_HACTIVE_AREA_OFST 28 +#define M00233_VIDEO_MEASURE_REG_HFRONT_PORCH_OFST 32 +#define M00233_VIDEO_MEASURE_REG_CONTROL_OFST 36 +#define M00233_VIDEO_MEASURE_REG_IRQ_TRIGGERS_OFST 40 +#define M00233_VIDEO_MEASURE_REG_HSYNC_TIMEOUT_VAL_OFST 44 +#define M00233_VIDEO_MEASURE_REG_STATUS_OFST 48 + +/******************************************************************* + * Bit Mask for register + * M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* irq_status [7:0] */ +#define M00233_IRQ_STATUS_BITMAP_VSYNC_TIME_OFST (0) +#define M00233_IRQ_STATUS_BITMAP_VSYNC_TIME_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_VSYNC_TIME_OFST) +#define M00233_IRQ_STATUS_BITMAP_VBACK_PORCH_OFST (1) +#define M00233_IRQ_STATUS_BITMAP_VBACK_PORCH_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_VBACK_PORCH_OFST) +#define M00233_IRQ_STATUS_BITMAP_VACTIVE_AREA_OFST (2) +#define M00233_IRQ_STATUS_BITMAP_VACTIVE_AREA_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_VACTIVE_AREA_OFST) +#define M00233_IRQ_STATUS_BITMAP_VFRONT_PORCH_OFST (3) +#define M00233_IRQ_STATUS_BITMAP_VFRONT_PORCH_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_VFRONT_PORCH_OFST) +#define M00233_IRQ_STATUS_BITMAP_HSYNC_TIME_OFST (4) +#define M00233_IRQ_STATUS_BITMAP_HSYNC_TIME_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_HSYNC_TIME_OFST) +#define M00233_IRQ_STATUS_BITMAP_HBACK_PORCH_OFST (5) +#define M00233_IRQ_STATUS_BITMAP_HBACK_PORCH_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_HBACK_PORCH_OFST) +#define M00233_IRQ_STATUS_BITMAP_HACTIVE_AREA_OFST (6) +#define M00233_IRQ_STATUS_BITMAP_HACTIVE_AREA_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_HACTIVE_AREA_OFST) +#define M00233_IRQ_STATUS_BITMAP_HFRONT_PORCH_OFST (7) +#define M00233_IRQ_STATUS_BITMAP_HFRONT_PORCH_MSK (0x1 << M00233_IRQ_STATUS_BITMAP_HFRONT_PORCH_OFST) +/* control [4:0] */ +#define M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST (0) +#define M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK (0x1 << M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST) +#define M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST (1) +#define M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK (0x1 << M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST) +#define M00233_CONTROL_BITMAP_ENABLE_MEASURE_OFST (2) +#define M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK (0x1 << M00233_CONTROL_BITMAP_ENABLE_MEASURE_OFST) +#define M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_OFST (3) +#define M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK (0x1 << M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_OFST) +#define M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_OFST (4) +#define M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_MSK (0x1 << M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_OFST) +/* irq_triggers [7:0] */ +#define M00233_IRQ_TRIGGERS_BITMAP_VSYNC_TIME_OFST (0) +#define M00233_IRQ_TRIGGERS_BITMAP_VSYNC_TIME_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_VSYNC_TIME_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_VBACK_PORCH_OFST (1) +#define M00233_IRQ_TRIGGERS_BITMAP_VBACK_PORCH_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_VBACK_PORCH_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_OFST (2) +#define M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_VFRONT_PORCH_OFST (3) +#define M00233_IRQ_TRIGGERS_BITMAP_VFRONT_PORCH_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_VFRONT_PORCH_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_HSYNC_TIME_OFST (4) +#define M00233_IRQ_TRIGGERS_BITMAP_HSYNC_TIME_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_HSYNC_TIME_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_HBACK_PORCH_OFST (5) +#define M00233_IRQ_TRIGGERS_BITMAP_HBACK_PORCH_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_HBACK_PORCH_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_OFST (6) +#define M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_OFST) +#define M00233_IRQ_TRIGGERS_BITMAP_HFRONT_PORCH_OFST (7) +#define M00233_IRQ_TRIGGERS_BITMAP_HFRONT_PORCH_MSK (0x1 << M00233_IRQ_TRIGGERS_BITMAP_HFRONT_PORCH_OFST) +/* status [1:0] */ +#define M00233_STATUS_BITMAP_HSYNC_TIMEOUT_OFST (0) +#define M00233_STATUS_BITMAP_HSYNC_TIMEOUT_MSK (0x1 << M00233_STATUS_BITMAP_HSYNC_TIMEOUT_OFST) +#define M00233_STATUS_BITMAP_INIT_DONE_OFST (1) +#define M00233_STATUS_BITMAP_INIT_DONE_MSK (0x1 << M00233_STATUS_BITMAP_INIT_DONE_OFST) + +#endif /*M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h new file mode 100644 index 000000000..a480529f5 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h @@ -0,0 +1,44 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00235_FDMA_PACKER_MEMMAP_PACKAGE_H +#define M00235_FDMA_PACKER_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00235_FDMA_PACKER_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00235_fdma_packer_regmap { + uint32_t control; /* Reg 0x0000, Default=0x0 */ +}; + +#define M00235_FDMA_PACKER_REG_CONTROL_OFST 0 + +/******************************************************************* + * Bit Mask for register + * M00235_FDMA_PACKER_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* control [3:0] */ +#define M00235_CONTROL_BITMAP_ENABLE_OFST (0) +#define M00235_CONTROL_BITMAP_ENABLE_MSK (0x1 << M00235_CONTROL_BITMAP_ENABLE_OFST) +#define M00235_CONTROL_BITMAP_PACK_FORMAT_OFST (1) +#define M00235_CONTROL_BITMAP_PACK_FORMAT_MSK (0x3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST) +#define M00235_CONTROL_BITMAP_ENDIAN_FORMAT_OFST (3) +#define M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK (0x1 << M00235_CONTROL_BITMAP_ENDIAN_FORMAT_OFST) + +#endif /*M00235_FDMA_PACKER_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h new file mode 100644 index 000000000..602419e58 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h @@ -0,0 +1,59 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00389_CVI_MEMMAP_PACKAGE_H +#define M00389_CVI_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00389_CVI_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00389_cvi_regmap { + uint32_t control; /* Reg 0x0000, Default=0x0 */ + uint32_t frame_width; /* Reg 0x0004, Default=0x10 */ + uint32_t frame_height; /* Reg 0x0008, Default=0xc */ + uint32_t freewheel_period; /* Reg 0x000c, Default=0x0 */ + uint32_t error_color; /* Reg 0x0010, Default=0x0 */ + uint32_t status; /* Reg 0x0014 */ +}; + +#define M00389_CVI_REG_CONTROL_OFST 0 +#define M00389_CVI_REG_FRAME_WIDTH_OFST 4 +#define M00389_CVI_REG_FRAME_HEIGHT_OFST 8 +#define M00389_CVI_REG_FREEWHEEL_PERIOD_OFST 12 +#define M00389_CVI_REG_ERROR_COLOR_OFST 16 +#define M00389_CVI_REG_STATUS_OFST 20 + +/******************************************************************* + * Bit Mask for register + * M00389_CVI_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* control [2:0] */ +#define M00389_CONTROL_BITMAP_ENABLE_OFST (0) +#define M00389_CONTROL_BITMAP_ENABLE_MSK (0x1 << M00389_CONTROL_BITMAP_ENABLE_OFST) +#define M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST (1) +#define M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK (0x1 << M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST) +#define M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST (2) +#define M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK (0x1 << M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST) +/* status [1:0] */ +#define M00389_STATUS_BITMAP_LOCK_OFST (0) +#define M00389_STATUS_BITMAP_LOCK_MSK (0x1 << M00389_STATUS_BITMAP_LOCK_OFST) +#define M00389_STATUS_BITMAP_ERROR_OFST (1) +#define M00389_STATUS_BITMAP_ERROR_MSK (0x1 << M00389_STATUS_BITMAP_ERROR_OFST) + +#endif /*M00389_CVI_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h new file mode 100644 index 000000000..95471c995 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h @@ -0,0 +1,44 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00460_EVCNT_MEMMAP_PACKAGE_H +#define M00460_EVCNT_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00460_EVCNT_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00460_evcnt_regmap { + uint32_t control; /* Reg 0x0000, Default=0x0 */ + uint32_t count; /* Reg 0x0004 */ +}; + +#define M00460_EVCNT_REG_CONTROL_OFST 0 +#define M00460_EVCNT_REG_COUNT_OFST 4 + +/******************************************************************* + * Bit Mask for register + * M00460_EVCNT_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* control [1:0] */ +#define M00460_CONTROL_BITMAP_ENABLE_OFST (0) +#define M00460_CONTROL_BITMAP_ENABLE_MSK (0x1 << M00460_CONTROL_BITMAP_ENABLE_OFST) +#define M00460_CONTROL_BITMAP_CLEAR_OFST (1) +#define M00460_CONTROL_BITMAP_CLEAR_MSK (0x1 << M00460_CONTROL_BITMAP_CLEAR_OFST) + +#endif /*M00460_EVCNT_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h new file mode 100644 index 000000000..384a3e156 --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h @@ -0,0 +1,57 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00473_FREEWHEEL_MEMMAP_PACKAGE_H +#define M00473_FREEWHEEL_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00473_FREEWHEEL_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00473_freewheel_regmap { + uint32_t ctrl; /* Reg 0x0000, Default=0x0 */ + uint32_t status; /* Reg 0x0004 */ + uint32_t active_length; /* Reg 0x0008, Default=0x1fa400 */ + uint32_t total_length; /* Reg 0x000c, Default=0x31151b */ + uint32_t data_width; /* Reg 0x0010 */ + uint32_t output_color; /* Reg 0x0014, Default=0xffff */ + uint32_t clk_freq; /* Reg 0x0018 */ +}; + +#define M00473_FREEWHEEL_REG_CTRL_OFST 0 +#define M00473_FREEWHEEL_REG_STATUS_OFST 4 +#define M00473_FREEWHEEL_REG_ACTIVE_LENGTH_OFST 8 +#define M00473_FREEWHEEL_REG_TOTAL_LENGTH_OFST 12 +#define M00473_FREEWHEEL_REG_DATA_WIDTH_OFST 16 +#define M00473_FREEWHEEL_REG_OUTPUT_COLOR_OFST 20 +#define M00473_FREEWHEEL_REG_CLK_FREQ_OFST 24 + +/******************************************************************* + * Bit Mask for register + * M00473_FREEWHEEL_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* ctrl [1:0] */ +#define M00473_CTRL_BITMAP_ENABLE_OFST (0) +#define M00473_CTRL_BITMAP_ENABLE_MSK (0x1 << M00473_CTRL_BITMAP_ENABLE_OFST) +#define M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_OFST (1) +#define M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK (0x1 << M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_OFST) +/* status [0:0] */ +#define M00473_STATUS_BITMAP_FREEWHEEL_MODE_OFST (0) +#define M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK (0x1 << M00473_STATUS_BITMAP_FREEWHEEL_MODE_OFST) + +#endif /*M00473_FREEWHEEL_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h new file mode 100644 index 000000000..2a029026b --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_H +#define M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00479_clk_loss_detector_regmap { + /* Control module */ + uint32_t ctrl; /* Reg 0x0000, Default=0x0 */ + uint32_t status; /* Reg 0x0004 */ + /* Number of ref clk cycles before checking the clock under test */ + uint32_t ref_clk_cnt_val; /* Reg 0x0008, Default=0xc4 */ + /* Number of test clk cycles required in the ref_clk_cnt_val period + * to ensure that the test clock is performing as expected */ + uint32_t test_clk_cnt_val; /* Reg 0x000c, Default=0xa */ +}; + +#define M00479_CLK_LOSS_DETECTOR_REG_CTRL_OFST 0 +#define M00479_CLK_LOSS_DETECTOR_REG_STATUS_OFST 4 +#define M00479_CLK_LOSS_DETECTOR_REG_REF_CLK_CNT_VAL_OFST 8 +#define M00479_CLK_LOSS_DETECTOR_REG_TEST_CLK_CNT_VAL_OFST 12 + +/******************************************************************* + * Bit Mask for register + * M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* ctrl [0:0] */ +#define M00479_CTRL_BITMAP_ENABLE_OFST (0) +#define M00479_CTRL_BITMAP_ENABLE_MSK (0x1 << M00479_CTRL_BITMAP_ENABLE_OFST) +/* status [0:0] */ +#define M00479_STATUS_BITMAP_CLOCK_MISSING_OFST (0) +#define M00479_STATUS_BITMAP_CLOCK_MISSING_MSK (0x1 << M00479_STATUS_BITMAP_CLOCK_MISSING_OFST) + +#endif /*M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h b/kernel/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h new file mode 100644 index 000000000..bdef2df5d --- /dev/null +++ b/kernel/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h @@ -0,0 +1,88 @@ +/* + * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. + * All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_H +#define M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_H + +/******************************************************************* + * Register Block + * M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_VHD_REGMAP + *******************************************************************/ +struct m00514_syncgen_flow_evcnt_regmap { + uint32_t control; /* Reg 0x0000, Default=0x0 */ + uint32_t sync_generator_h_sync_length; /* Reg 0x0004, Default=0x0 */ + uint32_t sync_generator_h_backporch_length; /* Reg 0x0008, Default=0x0 */ + uint32_t sync_generator_h_active_length; /* Reg 0x000c, Default=0x0 */ + uint32_t sync_generator_h_frontporch_length; /* Reg 0x0010, Default=0x0 */ + uint32_t sync_generator_v_sync_length; /* Reg 0x0014, Default=0x0 */ + uint32_t sync_generator_v_backporch_length; /* Reg 0x0018, Default=0x0 */ + uint32_t sync_generator_v_active_length; /* Reg 0x001c, Default=0x0 */ + uint32_t sync_generator_v_frontporch_length; /* Reg 0x0020, Default=0x0 */ + uint32_t error_color; /* Reg 0x0024, Default=0x0 */ + uint32_t rd_status; /* Reg 0x0028 */ + uint32_t rd_evcnt_count; /* Reg 0x002c */ +}; + +#define M00514_SYNCGEN_FLOW_EVCNT_REG_CONTROL_OFST 0 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_H_SYNC_LENGTH_OFST 4 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_H_BACKPORCH_LENGTH_OFST 8 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_H_ACTIVE_LENGTH_OFST 12 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_H_FRONTPORCH_LENGTH_OFST 16 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_V_SYNC_LENGTH_OFST 20 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_V_BACKPORCH_LENGTH_OFST 24 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_V_ACTIVE_LENGTH_OFST 28 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_SYNC_GENERATOR_V_FRONTPORCH_LENGTH_OFST 32 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_ERROR_COLOR_OFST 36 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_RD_STATUS_OFST 40 +#define M00514_SYNCGEN_FLOW_EVCNT_REG_RD_EVCNT_COUNT_OFST 44 + +/******************************************************************* + * Bit Mask for register + * M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_VHD_BITMAP + *******************************************************************/ +/* control [7:0] */ +#define M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_OFST (0) +#define M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_MSK (0x1 << M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_OFST) +#define M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_OFST (1) +#define M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_MSK (0x1 << M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_OFST) +#define M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_OFST (2) +#define M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_MSK (0x1 << M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_OFST) +#define M00514_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST (3) +#define M00514_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK (0x1 << M00514_CONTROL_BITMAP_HSYNC_POLARITY_LOW_OFST) +#define M00514_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST (4) +#define M00514_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK (0x1 << M00514_CONTROL_BITMAP_VSYNC_POLARITY_LOW_OFST) +#define M00514_CONTROL_BITMAP_EVCNT_ENABLE_OFST (5) +#define M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK (0x1 << M00514_CONTROL_BITMAP_EVCNT_ENABLE_OFST) +#define M00514_CONTROL_BITMAP_EVCNT_CLEAR_OFST (6) +#define M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK (0x1 << M00514_CONTROL_BITMAP_EVCNT_CLEAR_OFST) +#define M00514_CONTROL_BITMAP_FORMAT_16_BPP_OFST (7) +#define M00514_CONTROL_BITMAP_FORMAT_16_BPP_MSK (0x1 << M00514_CONTROL_BITMAP_FORMAT_16_BPP_OFST) +/* error_color [23:0] */ +#define M00514_ERROR_COLOR_BITMAP_BLUE_OFST (0) +#define M00514_ERROR_COLOR_BITMAP_BLUE_MSK (0xff << M00514_ERROR_COLOR_BITMAP_BLUE_OFST) +#define M00514_ERROR_COLOR_BITMAP_GREEN_OFST (8) +#define M00514_ERROR_COLOR_BITMAP_GREEN_MSK (0xff << M00514_ERROR_COLOR_BITMAP_GREEN_OFST) +#define M00514_ERROR_COLOR_BITMAP_RED_OFST (16) +#define M00514_ERROR_COLOR_BITMAP_RED_MSK (0xff << M00514_ERROR_COLOR_BITMAP_RED_OFST) +/* rd_status [1:0] */ +#define M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_OFST (0) +#define M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_MSK (0x1 << M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_OFST) +#define M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_OFST (1) +#define M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_MSK (0x1 << M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_OFST) + +#endif /*M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_H*/ diff --git a/kernel/drivers/media/pci/cx18/cx18-av-core.c b/kernel/drivers/media/pci/cx18/cx18-av-core.c index 5a55630d0..30bbe8d1e 100644 --- a/kernel/drivers/media/pci/cx18/cx18-av-core.c +++ b/kernel/drivers/media/pci/cx18/cx18-av-core.c @@ -945,14 +945,17 @@ static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) +static int cx18_av_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; struct cx18_av_state *state = to_cx18_av_state(sd); struct cx18 *cx = v4l2_get_subdevdata(sd); int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); - if (fmt->code != MEDIA_BUS_FMT_FIXED) + if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) return -EINVAL; fmt->field = V4L2_FIELD_INTERLACED; @@ -987,6 +990,9 @@ static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt return -ERANGE; } + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); VSC &= 0x1fff; @@ -1285,7 +1291,6 @@ static const struct v4l2_subdev_video_ops cx18_av_video_ops = { .s_std = cx18_av_s_std, .s_routing = cx18_av_s_video_routing, .s_stream = cx18_av_s_stream, - .s_mbus_fmt = cx18_av_s_mbus_fmt, }; static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { @@ -1295,12 +1300,17 @@ static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { .s_raw_fmt = cx18_av_s_raw_fmt, }; +static const struct v4l2_subdev_pad_ops cx18_av_pad_ops = { + .set_fmt = cx18_av_set_fmt, +}; + static const struct v4l2_subdev_ops cx18_av_ops = { .core = &cx18_av_general_ops, .tuner = &cx18_av_tuner_ops, .audio = &cx18_av_audio_ops, .video = &cx18_av_video_ops, .vbi = &cx18_av_vbi_ops, + .pad = &cx18_av_pad_ops, }; int cx18_av_probe(struct cx18 *cx) diff --git a/kernel/drivers/media/pci/cx18/cx18-controls.c b/kernel/drivers/media/pci/cx18/cx18-controls.c index 4aeb7c6b8..71227a155 100644 --- a/kernel/drivers/media/pci/cx18/cx18-controls.c +++ b/kernel/drivers/media/pci/cx18/cx18-controls.c @@ -93,13 +93,16 @@ static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) { struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *fmt = &format.format; /* fix videodecoder resolution */ - fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); - fmt.height = cxhdl->height; - fmt.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt); + fmt->width = cxhdl->width / (is_mpeg1 ? 2 : 1); + fmt->height = cxhdl->height; + fmt->code = MEDIA_BUS_FMT_FIXED; + v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format); return 0; } diff --git a/kernel/drivers/media/pci/cx18/cx18-driver.c b/kernel/drivers/media/pci/cx18/cx18-driver.c index 83f507470..260e462d9 100644 --- a/kernel/drivers/media/pci/cx18/cx18-driver.c +++ b/kernel/drivers/media/pci/cx18/cx18-driver.c @@ -786,11 +786,11 @@ static void cx18_init_struct2(struct cx18 *cx) { int i; - for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) + for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS - 1; i++) if (cx->card->video_inputs[i].video_type == 0) break; cx->nof_inputs = i; - for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) + for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS - 1; i++) if (cx->card->audio_inputs[i].audio_type == 0) break; cx->nof_audio_inputs = i; diff --git a/kernel/drivers/media/pci/cx18/cx18-ioctl.c b/kernel/drivers/media/pci/cx18/cx18-ioctl.c index 79aee30d5..55525af1f 100644 --- a/kernel/drivers/media/pci/cx18/cx18-ioctl.c +++ b/kernel/drivers/media/pci/cx18/cx18-ioctl.c @@ -267,7 +267,9 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, { struct cx18_open_id *id = fh2id(fh); struct cx18 *cx = id->cx; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct cx18_stream *s = &cx->streams[id->type]; int ret; int w, h; @@ -296,10 +298,10 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, s->vb_bytes_per_line = 1440; /* Packed */ } - mbus_fmt.width = cx->cxhdl.width = w; - mbus_fmt.height = cx->cxhdl.height = h; - mbus_fmt.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt); + format.format.width = cx->cxhdl.width = w; + format.format.height = cx->cxhdl.height = h; + format.format.code = MEDIA_BUS_FMT_FIXED; + v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format); return cx18_g_fmt_vid_cap(file, fh, fmt); } diff --git a/kernel/drivers/media/pci/cx18/cx18-mailbox.c b/kernel/drivers/media/pci/cx18/cx18-mailbox.c index eabf00c63..1f8aa9a74 100644 --- a/kernel/drivers/media/pci/cx18/cx18-mailbox.c +++ b/kernel/drivers/media/pci/cx18/cx18-mailbox.c @@ -202,7 +202,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, } if (dispatch) { - vb_buf->vb.ts = ktime_to_timeval(ktime_get()); + v4l2_get_timestamp(&vb_buf->vb.ts); list_del(&vb_buf->vb.queue); vb_buf->vb.state = VIDEOBUF_DONE; wake_up(&vb_buf->vb.done); diff --git a/kernel/drivers/media/pci/cx23885/altera-ci.c b/kernel/drivers/media/pci/cx23885/altera-ci.c index 0a91df2c9..aaf4e46ff 100644 --- a/kernel/drivers/media/pci/cx23885/altera-ci.c +++ b/kernel/drivers/media/pci/cx23885/altera-ci.c @@ -759,7 +759,7 @@ int altera_ci_init(struct altera_ci_config *config, int ci_nr) if (0 != ret) goto err; - inter->state[ci_nr - 1] = state; + inter->state[ci_nr - 1] = state; altera_hw_filt_init(config, ci_nr); diff --git a/kernel/drivers/media/pci/cx23885/cx23885-417.c b/kernel/drivers/media/pci/cx23885/cx23885-417.c index 63c0ee5d0..88a3afb66 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-417.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-417.c @@ -1138,7 +1138,7 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder) /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -1155,17 +1155,19 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); + container_of(vbuf, struct cx23885_buffer, vb); return cx23885_buf_prepare(buf, &dev->ts1); } static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_free_buffer(dev, buf); @@ -1173,8 +1175,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_buf_queue(&dev->ts1, buf); @@ -1201,7 +1204,7 @@ static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) struct cx23885_buffer, queue); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } spin_unlock_irqrestore(&dev->slock, flags); return ret; diff --git a/kernel/drivers/media/pci/cx23885/cx23885-core.c b/kernel/drivers/media/pci/cx23885/cx23885-core.c index 7aee76af7..e8f847226 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-core.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-core.c @@ -427,12 +427,13 @@ static void cx23885_wakeup(struct cx23885_tsport *port, buf = list_entry(q->active.next, struct cx23885_buffer, queue); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.sequence = q->count++; - dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.sequence = q->count++; + dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, + buf->vb.vb2_buf.index, count, q->count); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } int cx23885_sram_channel_setup(struct cx23885_dev *dev, @@ -1453,12 +1454,12 @@ int cx23885_buf_prepare(struct cx23885_buffer *buf, struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; int size = port->ts_packet_size * port->ts_packet_count; - struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0); + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); dprintk(1, "%s: %p\n", __func__, buf); - if (vb2_plane_size(&buf->vb, 0) < size) + if (vb2_plane_size(&buf->vb.vb2_buf, 0) < size) return -EINVAL; - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); cx23885_risc_databuffer(dev->pci, &buf->risc, sgt->sgl, @@ -1503,7 +1504,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) if (list_empty(&cx88q->active)) { list_add_tail(&buf->queue, &cx88q->active); dprintk(1, "[%p/%d] %s - first active\n", - buf, buf->vb.v4l2_buf.index, __func__); + buf, buf->vb.vb2_buf.index, __func__); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(cx88q->active.prev, struct cx23885_buffer, @@ -1511,7 +1512,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) list_add_tail(&buf->queue, &cx88q->active); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(1, "[%p/%d] %s - append to active\n", - buf, buf->vb.v4l2_buf.index, __func__); + buf, buf->vb.vb2_buf.index, __func__); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -1530,9 +1531,10 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason) buf = list_entry(q->active.next, struct cx23885_buffer, queue); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(1, "[%p/%d] %s - dma=0x%08lx\n", - buf, buf->vb.v4l2_buf.index, reason, (unsigned long)buf->risc.dma); + buf, buf->vb.vb2_buf.index, reason, + (unsigned long)buf->risc.dma); } spin_unlock_irqrestore(&port->slock, flags); } @@ -1990,9 +1992,9 @@ static int cx23885_initdev(struct pci_dev *pci_dev, (unsigned long long)pci_resource_start(pci_dev, 0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev, 0xffffffff)) { + err = pci_set_dma_mask(pci_dev, 0xffffffff); + if (err) { printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); - err = -EIO; goto fail_context; } diff --git a/kernel/drivers/media/pci/cx23885/cx23885-dvb.c b/kernel/drivers/media/pci/cx23885/cx23885-dvb.c index 745caabe3..c4307ad85 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-dvb.c @@ -92,7 +92,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -110,18 +110,20 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_tsport *port = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); + container_of(vbuf, struct cx23885_buffer, vb); return cx23885_buf_prepare(buf, port); } static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_tsport *port = vb->vb2_queue->drv_priv; struct cx23885_dev *dev = port->dev; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_free_buffer(dev, buf); @@ -129,8 +131,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_tsport *port = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_buf_queue(port, buf); @@ -572,7 +575,8 @@ static struct stb6100_config prof_8000_stb6100_config = { .refclock = 27000000, }; -static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int p8000_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct cx23885_tsport *port = fe->dvb->priv; struct cx23885_dev *dev = port->dev; @@ -587,7 +591,7 @@ static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) } static int dvbsky_t9580_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx23885_tsport *port = fe->dvb->priv; struct cx23885_dev *dev = port->dev; @@ -616,7 +620,7 @@ static int dvbsky_t9580_set_voltage(struct dvb_frontend *fe, } static int dvbsky_s952_portc_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx23885_tsport *port = fe->dvb->priv; struct cx23885_dev *dev = port->dev; @@ -856,18 +860,12 @@ static struct mt2063_config terratec_mt2063_config[] = { }, }; -static const struct tda10071_config hauppauge_tda10071_config = { - .demod_i2c_addr = 0x05, - .tuner_i2c_addr = 0x54, +static const struct tda10071_platform_data hauppauge_tda10071_pdata = { + .clk = 40444000, /* 40.444 MHz */ .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, - .spec_inv = 0, - .xtal = 40444000, /* 40.444 MHz */ .pll_multiplier = 20, -}; - -static const struct a8293_config hauppauge_a8293_config = { - .i2c_addr = 0x0b, + .tuner_i2c_addr = 0x54, }; static const struct si2165_config hauppauge_hvr4400_si2165_config = { @@ -1190,8 +1188,10 @@ static int dvb_register(struct cx23885_tsport *port) struct i2c_board_info info; struct i2c_adapter *adapter; struct i2c_client *client_demod = NULL, *client_tuner = NULL; + struct i2c_client *client_sec = NULL; const struct m88ds3103_config *p_m88ds3103_config = NULL; - int (*p_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage) = NULL; + int (*p_set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) = NULL; int mfe_shared = 0; /* bus not shared by default */ int ret; @@ -1797,21 +1797,46 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; break; - case CX23885_BOARD_HAUPPAUGE_HVR4400: + case CX23885_BOARD_HAUPPAUGE_HVR4400: { + struct tda10071_platform_data tda10071_pdata = hauppauge_tda10071_pdata; + struct a8293_platform_data a8293_pdata = {}; + i2c_bus = &dev->i2c_bus[0]; i2c_bus2 = &dev->i2c_bus[1]; switch (port->nr) { /* port b */ case 1: - fe0->dvb.frontend = dvb_attach(tda10071_attach, - &hauppauge_tda10071_config, - &i2c_bus->i2c_adap); - if (fe0->dvb.frontend == NULL) - break; - if (!dvb_attach(a8293_attach, fe0->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_a8293_config)) + /* attach demod + tuner combo */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, "tda10071_cx24118", I2C_NAME_SIZE); + info.addr = 0x05; + info.platform_data = &tda10071_pdata; + request_module("tda10071"); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + fe0->dvb.frontend = tda10071_pdata.get_dvb_frontend(client_demod); + port->i2c_client_demod = client_demod; + + /* attach SEC */ + a8293_pdata.dvb_frontend = fe0->dvb.frontend; + memset(&info, 0, sizeof(info)); + strlcpy(info.type, "a8293", I2C_NAME_SIZE); + info.addr = 0x0b; + info.platform_data = &a8293_pdata; + request_module("a8293"); + client_sec = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (!client_sec || !client_sec->dev.driver) + goto frontend_detach; + if (!try_module_get(client_sec->dev.driver->owner)) { + i2c_unregister_device(client_sec); goto frontend_detach; + } + port->i2c_client_sec = client_sec; break; /* port c */ case 2: @@ -1829,17 +1854,46 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; - case CX23885_BOARD_HAUPPAUGE_STARBURST: + } + case CX23885_BOARD_HAUPPAUGE_STARBURST: { + struct tda10071_platform_data tda10071_pdata = hauppauge_tda10071_pdata; + struct a8293_platform_data a8293_pdata = {}; + i2c_bus = &dev->i2c_bus[0]; - fe0->dvb.frontend = dvb_attach(tda10071_attach, - &hauppauge_tda10071_config, - &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) { - dvb_attach(a8293_attach, fe0->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_a8293_config); + + /* attach demod + tuner combo */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, "tda10071_cx24118", I2C_NAME_SIZE); + info.addr = 0x05; + info.platform_data = &tda10071_pdata; + request_module("tda10071"); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; } + fe0->dvb.frontend = tda10071_pdata.get_dvb_frontend(client_demod); + port->i2c_client_demod = client_demod; + + /* attach SEC */ + a8293_pdata.dvb_frontend = fe0->dvb.frontend; + memset(&info, 0, sizeof(info)); + strlcpy(info.type, "a8293", I2C_NAME_SIZE); + info.addr = 0x0b; + info.platform_data = &a8293_pdata; + request_module("a8293"); + client_sec = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (!client_sec || !client_sec->dev.driver) + goto frontend_detach; + if (!try_module_get(client_sec->dev.driver->owner)) { + i2c_unregister_device(client_sec); + goto frontend_detach; + } + port->i2c_client_sec = client_sec; break; + } case CX23885_BOARD_DVBSKY_T9580: case CX23885_BOARD_DVBSKY_S950: i2c_bus = &dev->i2c_bus[0]; @@ -1857,6 +1911,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&ts2020_config, 0, sizeof(ts2020_config)); ts2020_config.fe = fe0->dvb.frontend; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ts2020", I2C_NAME_SIZE); info.addr = 0x60; @@ -1912,6 +1967,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -1957,6 +2013,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -1986,6 +2043,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&ts2020_config, 0, sizeof(ts2020_config)); ts2020_config.fe = fe0->dvb.frontend; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ts2020", I2C_NAME_SIZE); info.addr = 0x60; @@ -2031,6 +2089,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&ts2020_config, 0, sizeof(ts2020_config)); ts2020_config.fe = fe0->dvb.frontend; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "ts2020", I2C_NAME_SIZE); info.addr = 0x60; @@ -2093,6 +2152,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -2111,6 +2171,7 @@ static int dvb_register(struct cx23885_tsport *port) case CX23885_BOARD_HAUPPAUGE_HVR5525: switch (port->nr) { struct m88rs6000t_config m88rs6000t_config; + struct a8293_platform_data a8293_pdata = {}; /* port b - satellite */ case 1: @@ -2122,10 +2183,20 @@ static int dvb_register(struct cx23885_tsport *port) break; /* attach SEC */ - if (!dvb_attach(a8293_attach, fe0->dvb.frontend, - &dev->i2c_bus[0].i2c_adap, - &hauppauge_a8293_config)) + a8293_pdata.dvb_frontend = fe0->dvb.frontend; + memset(&info, 0, sizeof(info)); + strlcpy(info.type, "a8293", I2C_NAME_SIZE); + info.addr = 0x0b; + info.platform_data = &a8293_pdata; + request_module("a8293"); + client_sec = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (!client_sec || !client_sec->dev.driver) + goto frontend_detach; + if (!try_module_get(client_sec->dev.driver->owner)) { + i2c_unregister_device(client_sec); goto frontend_detach; + } + port->i2c_client_sec = client_sec; /* attach tuner */ memset(&m88rs6000t_config, 0, sizeof(m88rs6000t_config)); @@ -2172,6 +2243,7 @@ static int dvb_register(struct cx23885_tsport *port) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -2238,6 +2310,14 @@ static int dvb_register(struct cx23885_tsport *port) return 0; frontend_detach: + /* remove I2C client for SEC */ + client_sec = port->i2c_client_sec; + if (client_sec) { + module_put(client_sec->dev.driver->owner); + i2c_unregister_device(client_sec); + port->i2c_client_sec = NULL; + } + /* remove I2C client for tuner */ client_tuner = port->i2c_client_tuner; if (client_tuner) { @@ -2339,6 +2419,13 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) i2c_unregister_device(client); } + /* remove I2C client for SEC */ + client = port->i2c_client_sec; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + /* remove I2C client for tuner */ client = port->i2c_client_tuner; if (client) { diff --git a/kernel/drivers/media/pci/cx23885/cx23885-f300.c b/kernel/drivers/media/pci/cx23885/cx23885-f300.c index 6f817d873..a6c45eb0a 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-f300.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-f300.c @@ -144,7 +144,7 @@ static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) return ret; } -int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +int f300_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { u8 buf[16]; diff --git a/kernel/drivers/media/pci/cx23885/cx23885-f300.h b/kernel/drivers/media/pci/cx23885/cx23885-f300.h index e73344c94..be14d7de7 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-f300.h +++ b/kernel/drivers/media/pci/cx23885/cx23885-f300.h @@ -1,2 +1,2 @@ extern int f300_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage); + enum fe_sec_voltage voltage); diff --git a/kernel/drivers/media/pci/cx23885/cx23885-vbi.c b/kernel/drivers/media/pci/cx23885/cx23885-vbi.c index d362d3838..cf3cb1324 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-vbi.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-vbi.c @@ -121,7 +121,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -138,8 +138,9 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); unsigned lines = VBI_PAL_LINE_COUNT; @@ -161,7 +162,8 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_buffer *buf = container_of(vb, + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); @@ -190,8 +192,10 @@ static void buffer_finish(struct vb2_buffer *vb) */ static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); + struct cx23885_buffer *buf = container_of(vbuf, + struct cx23885_buffer, vb); struct cx23885_buffer *prev; struct cx23885_dmaqueue *q = &dev->vbiq; unsigned long flags; @@ -206,7 +210,7 @@ static void buffer_queue(struct vb2_buffer *vb) list_add_tail(&buf->queue, &q->active); spin_unlock_irqrestore(&dev->slock, flags); dprintk(2, "[%p/%d] vbi_queue - first active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); @@ -217,7 +221,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&dev->slock, flags); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } } @@ -245,7 +249,7 @@ static void cx23885_stop_streaming(struct vb2_queue *q) struct cx23885_buffer, queue); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } diff --git a/kernel/drivers/media/pci/cx23885/cx23885-video.c b/kernel/drivers/media/pci/cx23885/cx23885-video.c index 2232b389c..71a80e2b8 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885-video.c +++ b/kernel/drivers/media/pci/cx23885/cx23885-video.c @@ -104,12 +104,12 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, buf = list_entry(q->active.next, struct cx23885_buffer, queue); - buf->vb.v4l2_buf.sequence = q->count++; - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, - count, q->count); + buf->vb.sequence = q->count++; + v4l2_get_timestamp(&buf->vb.timestamp); + dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, + buf->vb.vb2_buf.index, count, q->count); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) @@ -315,7 +315,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, return 0; } -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -329,9 +329,10 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); + container_of(vbuf, struct cx23885_buffer, vb); u32 line0_offset, line1_offset; struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); int field_tff; @@ -401,7 +402,7 @@ static int buffer_prepare(struct vb2_buffer *vb) BUG(); } dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.v4l2_buf.index, + buf, buf->vb.vb2_buf.index, dev->width, dev->height, dev->fmt->depth, dev->fmt->name, (unsigned long)buf->risc.dma); return 0; @@ -409,7 +410,8 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_buffer *buf = container_of(vb, + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); @@ -438,8 +440,9 @@ static void buffer_finish(struct vb2_buffer *vb) */ static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; - struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer *buf = container_of(vbuf, struct cx23885_buffer, vb); struct cx23885_buffer *prev; struct cx23885_dmaqueue *q = &dev->vidq; @@ -455,7 +458,7 @@ static void buffer_queue(struct vb2_buffer *vb) if (list_empty(&q->active)) { list_add_tail(&buf->queue, &q->active); dprintk(2, "[%p/%d] buffer_queue - first active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx23885_buffer, @@ -463,7 +466,7 @@ static void buffer_queue(struct vb2_buffer *vb) list_add_tail(&buf->queue, &q->active); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -492,7 +495,7 @@ static void cx23885_stop_streaming(struct vb2_queue *q) struct cx23885_buffer, queue); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -581,7 +584,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx23885_dev *dev = video_drvdata(file); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int err; dprintk(2, "%s()\n", __func__); @@ -600,10 +605,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, dev->field = f->fmt.pix.field; dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, dev->width, dev->height, dev->field); - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); - /* s_mbus_fmt overwrites f->fmt.pix.field, restore it */ + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); + call_all(dev, pad, set_fmt, NULL, &format); + v4l2_fill_pix_format(&f->fmt.pix, &format.format); + /* set_fmt overwrites f->fmt.pix.field, restore it */ f->fmt.pix.field = dev->field; return 0; } diff --git a/kernel/drivers/media/pci/cx23885/cx23885.h b/kernel/drivers/media/pci/cx23885/cx23885.h index aeda8d399..c5ba0833f 100644 --- a/kernel/drivers/media/pci/cx23885/cx23885.h +++ b/kernel/drivers/media/pci/cx23885/cx23885.h @@ -170,7 +170,7 @@ struct cx23885_riscmem { /* buffer for one video frame */ struct cx23885_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head queue; /* cx23885 specific */ @@ -304,11 +304,12 @@ struct cx23885_tsport { struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; + struct i2c_client *i2c_client_sec; struct i2c_client *i2c_client_ci; int (*set_frontend)(struct dvb_frontend *fe); int (*fe_set_voltage)(struct dvb_frontend *fe, - fe_sec_voltage_t voltage); + enum fe_sec_voltage voltage); }; struct cx23885_kernel_ir { diff --git a/kernel/drivers/media/pci/cx25821/cx25821-alsa.c b/kernel/drivers/media/pci/cx25821/cx25821-alsa.c index 24f964bcc..b602eba2b 100644 --- a/kernel/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/kernel/drivers/media/pci/cx25821/cx25821-alsa.c @@ -102,7 +102,7 @@ struct cx25821_audio_dev { static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static bool enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 }; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); diff --git a/kernel/drivers/media/pci/cx25821/cx25821-core.c b/kernel/drivers/media/pci/cx25821/cx25821-core.c index 559f8293c..0042803a9 100644 --- a/kernel/drivers/media/pci/cx25821/cx25821-core.c +++ b/kernel/drivers/media/pci/cx25821/cx25821-core.c @@ -1319,7 +1319,8 @@ static int cx25821_initdev(struct pci_dev *pci_dev, dev->pci_lat, (unsigned long long)dev->base_io_addr); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev, 0xffffffff)) { + err = pci_set_dma_mask(pci_dev, 0xffffffff); + if (err) { pr_err("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); err = -EIO; goto fail_irq; diff --git a/kernel/drivers/media/pci/cx25821/cx25821-medusa-reg.h b/kernel/drivers/media/pci/cx25821/cx25821-medusa-reg.h index c98ac946b..2e10643a8 100644 --- a/kernel/drivers/media/pci/cx25821/cx25821-medusa-reg.h +++ b/kernel/drivers/media/pci/cx25821/cx25821-medusa-reg.h @@ -84,9 +84,9 @@ #define ABIST_BIN4_VGA3 0x01D4 #define ABIST_BIN5_VGA4 0x01D8 #define ABIST_BIN6_VGA5 0x01DC -#define ABIST_BIN7_VGA6 0x0x1E0 -#define ABIST_CLAMP_A 0x0x1E4 -#define ABIST_CLAMP_B 0x0x1E8 +#define ABIST_BIN7_VGA6 0x01E0 +#define ABIST_CLAMP_A 0x01E4 +#define ABIST_CLAMP_B 0x01E8 #define ABIST_CLAMP_C 0x01EC #define ABIST_CLAMP_D 0x01F0 #define ABIST_CLAMP_E 0x01F4 diff --git a/kernel/drivers/media/pci/cx25821/cx25821-video.c b/kernel/drivers/media/pci/cx25821/cx25821-video.c index 7bc495e4e..26e3e296d 100644 --- a/kernel/drivers/media/pci/cx25821/cx25821-video.c +++ b/kernel/drivers/media/pci/cx25821/cx25821-video.c @@ -130,10 +130,10 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) buf = list_entry(dmaq->active.next, struct cx25821_buffer, queue); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.sequence = dmaq->count++; + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.sequence = dmaq->count++; list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } spin_unlock(&dev->slock); handled++; @@ -141,10 +141,11 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) return handled; } -static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int cx25821_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct cx25821_channel *chan = q->drv_priv; unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3; @@ -159,10 +160,11 @@ static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fm static int cx25821_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); + container_of(vbuf, struct cx25821_buffer, vb); struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); u32 line0_offset; int bpl_local = LINE_SIZE_D1; @@ -176,7 +178,7 @@ static int cx25821_buffer_prepare(struct vb2_buffer *vb) if (vb2_plane_size(vb, 0) < chan->height * buf->bpl) return -EINVAL; vb2_set_plane_payload(vb, 0, chan->height * buf->bpl); - buf->vb.v4l2_buf.field = chan->field; + buf->vb.field = chan->field; if (chan->pixel_formats == PIXEL_FRMT_411) { bpl_local = buf->bpl; @@ -231,7 +233,7 @@ static int cx25821_buffer_prepare(struct vb2_buffer *vb) } dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.v4l2_buf.index, chan->width, chan->height, + buf, buf->vb.vb2_buf.index, chan->width, chan->height, chan->fmt->depth, chan->fmt->name, (unsigned long)buf->risc.dma); @@ -240,8 +242,9 @@ static int cx25821_buffer_prepare(struct vb2_buffer *vb) static void cx25821_buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); + container_of(vbuf, struct cx25821_buffer, vb); struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; @@ -250,8 +253,9 @@ static void cx25821_buffer_finish(struct vb2_buffer *vb) static void cx25821_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); + container_of(vbuf, struct cx25821_buffer, vb); struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; struct cx25821_buffer *prev; @@ -300,7 +304,7 @@ static void cx25821_stop_streaming(struct vb2_queue *q) struct cx25821_buffer, queue); list_del(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } diff --git a/kernel/drivers/media/pci/cx25821/cx25821.h b/kernel/drivers/media/pci/cx25821/cx25821.h index d81a08a2d..a513b68be 100644 --- a/kernel/drivers/media/pci/cx25821/cx25821.h +++ b/kernel/drivers/media/pci/cx25821/cx25821.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "cx25821-reg.h" @@ -127,7 +128,7 @@ struct cx25821_riscmem { /* buffer for one video frame */ struct cx25821_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head queue; /* cx25821 specific */ diff --git a/kernel/drivers/media/pci/cx88/cx88-alsa.c b/kernel/drivers/media/pci/cx88/cx88-alsa.c index 7f8dc6002..1b5268f9b 100644 --- a/kernel/drivers/media/pci/cx88/cx88-alsa.c +++ b/kernel/drivers/media/pci/cx88/cx88-alsa.c @@ -101,7 +101,7 @@ typedef struct cx88_audio_dev snd_cx88_card_t; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled."); @@ -890,9 +890,9 @@ static int snd_cx88_create(struct snd_card *card, struct pci_dev *pci, return err; } - if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { + err = pci_set_dma_mask(pci,DMA_BIT_MASK(32)); + if (err) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); - err = -EIO; cx88_core_put(core, pci); return err; } diff --git a/kernel/drivers/media/pci/cx88/cx88-blackbird.c b/kernel/drivers/media/pci/cx88/cx88-blackbird.c index 24216efa5..8b889135b 100644 --- a/kernel/drivers/media/pci/cx88/cx88-blackbird.c +++ b/kernel/drivers/media/pci/cx88/cx88-blackbird.c @@ -637,7 +637,7 @@ static int blackbird_stop_codec(struct cx8802_dev *dev) /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -653,16 +653,18 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); return cx8802_buf_prepare(vb->vb2_queue, dev, buf); } static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_riscmem *risc = &buf->risc; if (risc->cpu) @@ -672,8 +674,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); cx8802_buf_queue(dev, buf); } @@ -721,7 +724,7 @@ fail: struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } spin_unlock_irqrestore(&dev->slock, flags); return err; @@ -749,7 +752,7 @@ static void stop_streaming(struct vb2_queue *q) struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } diff --git a/kernel/drivers/media/pci/cx88/cx88-core.c b/kernel/drivers/media/pci/cx88/cx88-core.c index 3501be9f1..9a43c7826 100644 --- a/kernel/drivers/media/pci/cx88/cx88-core.c +++ b/kernel/drivers/media/pci/cx88/cx88-core.c @@ -518,9 +518,11 @@ void cx88_wakeup(struct cx88_core *core, buf = list_entry(q->active.next, struct cx88_buffer, list); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.field = core->field; + buf->vb.sequence = q->count++; list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } void cx88_shutdown(struct cx88_core *core) diff --git a/kernel/drivers/media/pci/cx88/cx88-dvb.c b/kernel/drivers/media/pci/cx88/cx88-dvb.c index 1b2ed238c..f04835073 100644 --- a/kernel/drivers/media/pci/cx88/cx88-dvb.c +++ b/kernel/drivers/media/pci/cx88/cx88-dvb.c @@ -82,7 +82,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -99,16 +99,18 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); return cx8802_buf_prepare(vb->vb2_queue, dev, buf); } static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_riscmem *risc = &buf->risc; if (risc->cpu) @@ -118,8 +120,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8802_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); cx8802_buf_queue(dev, buf); } @@ -149,7 +152,7 @@ static void stop_streaming(struct vb2_queue *q) struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -449,7 +452,7 @@ static int cx24123_set_ts_param(struct dvb_frontend* fe, } static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; @@ -465,7 +468,7 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, } static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; @@ -481,7 +484,7 @@ static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, } static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; @@ -505,7 +508,7 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, } static int vp1027_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx8802_dev *dev = fe->dvb->priv; struct cx88_core *core = dev->core; @@ -897,7 +900,7 @@ static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe) } static int samsung_smt_7020_set_tone(struct dvb_frontend *fe, - fe_sec_tone_mode_t tone) + enum fe_sec_tone_mode tone) { struct cx8802_dev *dev = fe->dvb->priv; struct cx88_core *core = dev->core; @@ -919,7 +922,7 @@ static int samsung_smt_7020_set_tone(struct dvb_frontend *fe, } static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct cx8802_dev *dev = fe->dvb->priv; struct cx88_core *core = dev->core; diff --git a/kernel/drivers/media/pci/cx88/cx88-mpeg.c b/kernel/drivers/media/pci/cx88/cx88-mpeg.c index 98344540c..f34c229f9 100644 --- a/kernel/drivers/media/pci/cx88/cx88-mpeg.c +++ b/kernel/drivers/media/pci/cx88/cx88-mpeg.c @@ -173,7 +173,7 @@ int cx8802_start_dma(struct cx8802_dev *dev, /* reset counter */ cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); - q->count = 1; + q->count = 0; /* enable irqs */ dprintk( 1, "setting the interrupt mask\n" ); @@ -214,10 +214,8 @@ static int cx8802_restart_queue(struct cx8802_dev *dev, buf = list_entry(q->active.next, struct cx88_buffer, list); dprintk(2,"restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); cx8802_start_dma(dev, q, buf); - list_for_each_entry(buf, &q->active, list) - buf->count = q->count++; return 0; } @@ -227,13 +225,13 @@ int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev, struct cx88_buffer *buf) { int size = dev->ts_packet_size * dev->ts_packet_count; - struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0); + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); struct cx88_riscmem *risc = &buf->risc; int rc; - if (vb2_plane_size(&buf->vb, 0) < size) + if (vb2_plane_size(&buf->vb.vb2_buf, 0) < size) return -EINVAL; - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl, dev->ts_packet_size, dev->ts_packet_count, 0); @@ -260,19 +258,17 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) if (list_empty(&cx88q->active)) { dprintk( 1, "queue is empty - first active\n" ); list_add_tail(&buf->list, &cx88q->active); - buf->count = cx88q->count++; dprintk(1,"[%p/%d] %s - first active\n", - buf, buf->vb.v4l2_buf.index, __func__); + buf, buf->vb.vb2_buf.index, __func__); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); dprintk( 1, "queue is not empty - append to active\n" ); prev = list_entry(cx88q->active.prev, struct cx88_buffer, list); list_add_tail(&buf->list, &cx88q->active); - buf->count = cx88q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk( 1, "[%p/%d] %s - append to active\n", - buf, buf->vb.v4l2_buf.index, __func__); + buf, buf->vb.vb2_buf.index, __func__); } } @@ -288,7 +284,7 @@ static void do_cancel_buffers(struct cx8802_dev *dev) while (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock,flags); } @@ -397,7 +393,8 @@ static int cx8802_init_common(struct cx8802_dev *dev) if (pci_enable_device(dev->pci)) return -EIO; pci_set_master(dev->pci); - if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) { + err = pci_set_dma_mask(dev->pci,DMA_BIT_MASK(32)); + if (err) { printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); return -EIO; } diff --git a/kernel/drivers/media/pci/cx88/cx88-vbi.c b/kernel/drivers/media/pci/cx88/cx88-vbi.c index 32eb7fdb8..007a5eee8 100644 --- a/kernel/drivers/media/pci/cx88/cx88-vbi.c +++ b/kernel/drivers/media/pci/cx88/cx88-vbi.c @@ -59,7 +59,7 @@ static int cx8800_start_vbi_dma(struct cx8800_dev *dev, /* reset counter */ cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET); - q->count = 1; + q->count = 0; /* enable irqs */ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); @@ -100,16 +100,14 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev, buf = list_entry(q->active.next, struct cx88_buffer, list); dprintk(2,"restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); cx8800_start_vbi_dma(dev, q, buf); - list_for_each_entry(buf, &q->active, list) - buf->count = q->count++; return 0; } /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -127,8 +125,9 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); unsigned int lines; unsigned int size; @@ -151,8 +150,9 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_riscmem *risc = &buf->risc; if (risc->cpu) @@ -162,8 +162,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_buffer *prev; struct cx88_dmaqueue *q = &dev->vbiq; @@ -175,18 +176,16 @@ static void buffer_queue(struct vb2_buffer *vb) if (list_empty(&q->active)) { list_add_tail(&buf->list, &q->active); cx8800_start_vbi_dma(dev, q, buf); - buf->count = q->count++; dprintk(2,"[%p/%d] vbi_queue - first active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx88_buffer, list); list_add_tail(&buf->list, &q->active); - buf->count = q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(2,"[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } } @@ -217,7 +216,7 @@ static void stop_streaming(struct vb2_queue *q) struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } diff --git a/kernel/drivers/media/pci/cx88/cx88-video.c b/kernel/drivers/media/pci/cx88/cx88-video.c index c9decd80b..aef9acf35 100644 --- a/kernel/drivers/media/pci/cx88/cx88-video.c +++ b/kernel/drivers/media/pci/cx88/cx88-video.c @@ -370,7 +370,7 @@ static int start_video_dma(struct cx8800_dev *dev, /* reset counter */ cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET); - q->count = 1; + q->count = 0; /* enable irqs */ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); @@ -410,7 +410,6 @@ static int stop_video_dma(struct cx8800_dev *dev) cx_clear(MO_VID_INTMSK, 0x0f0011); return 0; } -#endif static int restart_video_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q) @@ -421,17 +420,16 @@ static int restart_video_queue(struct cx8800_dev *dev, if (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx88_buffer, list); dprintk(2,"restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); start_video_dma(dev, q, buf); - list_for_each_entry(buf, &q->active, list) - buf->count = q->count++; } return 0; } +#endif /* ------------------------------------------------------------------ */ -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -446,9 +444,10 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; struct cx88_core *core = dev->core; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); buf->bpl = core->width * dev->fmt->depth >> 3; @@ -491,7 +490,7 @@ static int buffer_prepare(struct vb2_buffer *vb) break; } dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.v4l2_buf.index, + buf, buf->vb.vb2_buf.index, core->width, core->height, dev->fmt->depth, dev->fmt->name, (unsigned long)buf->risc.dma); return 0; @@ -499,8 +498,9 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_riscmem *risc = &buf->risc; if (risc->cpu) @@ -510,8 +510,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; - struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb); + struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); struct cx88_buffer *prev; struct cx88_core *core = dev->core; struct cx88_dmaqueue *q = &dev->vidq; @@ -523,18 +524,16 @@ static void buffer_queue(struct vb2_buffer *vb) if (list_empty(&q->active)) { list_add_tail(&buf->list, &q->active); - buf->count = q->count++; dprintk(2,"[%p/%d] buffer_queue - first active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } else { buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx88_buffer, list); list_add_tail(&buf->list, &q->active); - buf->count = q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } } @@ -564,7 +563,7 @@ static void stop_streaming(struct vb2_queue *q) struct cx88_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -771,6 +770,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1314,9 +1314,9 @@ static int cx8800_initdev(struct pci_dev *pci_dev, dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) { + err = pci_set_dma_mask(pci_dev,DMA_BIT_MASK(32)); + if (err) { printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); - err = -EIO; goto fail_core; } dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); diff --git a/kernel/drivers/media/pci/cx88/cx88.h b/kernel/drivers/media/pci/cx88/cx88.h index b9fe1ac24..2996eb3ea 100644 --- a/kernel/drivers/media/pci/cx88/cx88.h +++ b/kernel/drivers/media/pci/cx88/cx88.h @@ -321,13 +321,12 @@ struct cx88_riscmem { /* buffer for one video frame */ struct cx88_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; /* cx88 specific */ unsigned int bpl; struct cx88_riscmem risc; - u32 count; }; struct cx88_dmaqueue { @@ -376,9 +375,10 @@ struct cx88_core { /* config info -- dvb */ #if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) - int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*prev_set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); #endif - void (*gate_ctrl)(struct cx88_core *core, int open); + void (*gate_ctrl)(struct cx88_core *core, int open); /* state info */ struct task_struct *kthread; diff --git a/kernel/drivers/media/pci/ddbridge/ddbridge-core.c b/kernel/drivers/media/pci/ddbridge/ddbridge-core.c index 9e3492e20..0ac2dd35f 100644 --- a/kernel/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/kernel/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1630,7 +1630,8 @@ fail1: printk(KERN_ERR "fail1\n"); if (dev->msi) pci_disable_msi(dev->pdev); - free_irq(dev->pdev->irq, dev); + if (stat == 0) + free_irq(dev->pdev->irq, dev); fail: printk(KERN_ERR "fail\n"); ddb_unmap(dev); diff --git a/kernel/drivers/media/pci/dm1105/dm1105.c b/kernel/drivers/media/pci/dm1105/dm1105.c index ed1171673..88915fb87 100644 --- a/kernel/drivers/media/pci/dm1105/dm1105.c +++ b/kernel/drivers/media/pci/dm1105/dm1105.c @@ -591,7 +591,8 @@ static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); } -static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int dm1105_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); diff --git a/kernel/drivers/media/pci/dt3155/Kconfig b/kernel/drivers/media/pci/dt3155/Kconfig new file mode 100644 index 000000000..5145e0dfa --- /dev/null +++ b/kernel/drivers/media/pci/dt3155/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_DT3155 + tristate "DT3155 frame grabber" + depends on PCI && VIDEO_DEV && VIDEO_V4L2 + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + default n + ---help--- + Enables dt3155 device driver for the DataTranslation DT3155 frame grabber. + Say Y here if you have this hardware. + In doubt, say N. + + To compile this driver as a module, choose M here: the + module will be called dt3155. diff --git a/kernel/drivers/media/pci/dt3155/Makefile b/kernel/drivers/media/pci/dt3155/Makefile new file mode 100644 index 000000000..89fa637ec --- /dev/null +++ b/kernel/drivers/media/pci/dt3155/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_DT3155) += dt3155.o diff --git a/kernel/drivers/media/pci/dt3155/dt3155.c b/kernel/drivers/media/pci/dt3155/dt3155.c new file mode 100644 index 000000000..d84abde5e --- /dev/null +++ b/kernel/drivers/media/pci/dt3155/dt3155.c @@ -0,0 +1,633 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Marin Mitov * + * mitov@issp.bas.bg * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dt3155.h" + +#define DT3155_DEVICE_ID 0x1223 + +/** + * read_i2c_reg - reads an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: pointer to byte the read data will be placed in + * + * returns: zero on success or error code + * + * This function starts reading the specified (by index) register + * and busy waits for the process to finish. The result is placed + * in a byte pointed by data. + */ +static int read_i2c_reg(void __iomem *addr, u8 index, u8 *data) +{ + u32 tmp = index; + + iowrite32((tmp << 17) | IIC_READ, addr + IIC_CSR2); + mmiowb(); + udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + tmp = ioread32(addr + IIC_CSR1); + if (tmp & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + *data = tmp >> 24; + return 0; +} + +/** + * write_i2c_reg - writes to an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: data to be written + * + * returns: zero on success or error code + * + * This function starts writing the specified (by index) register + * and busy waits for the process to finish. + */ +static int write_i2c_reg(void __iomem *addr, u8 index, u8 data) +{ + u32 tmp = index; + + iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2); + mmiowb(); + udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + return 0; +} + +/** + * write_i2c_reg_nowait - writes to an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: data to be written + * + * This function starts writing the specified (by index) register + * and then returns. + */ +static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data) +{ + u32 tmp = index; + + iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2); + mmiowb(); +} + +/** + * wait_i2c_reg - waits the read/write to finish + * + * @addr: dt3155 mmio base address + * + * returns: zero on success or error code + * + * This function waits reading/writing to finish. + */ +static int wait_i2c_reg(void __iomem *addr) +{ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + return 0; +} + +static int +dt3155_queue_setup(struct vb2_queue *vq, const void *parg, + unsigned int *nbuffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) + +{ + const struct v4l2_format *fmt = parg; + struct dt3155_priv *pd = vb2_get_drv_priv(vq); + unsigned size = pd->width * pd->height; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + if (fmt && fmt->fmt.pix.sizeimage < size) + return -EINVAL; + *num_planes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size; + alloc_ctxs[0] = pd->alloc_ctx; + return 0; +} + +static int dt3155_buf_prepare(struct vb2_buffer *vb) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); + + vb2_set_plane_payload(vb, 0, pd->width * pd->height); + return 0; +} + +static int dt3155_start_streaming(struct vb2_queue *q, unsigned count) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + struct vb2_buffer *vb = &pd->curr_buf->vb2_buf; + dma_addr_t dma_addr; + + pd->sequence = 0; + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + iowrite32(dma_addr, pd->regs + EVEN_DMA_START); + iowrite32(dma_addr + pd->width, pd->regs + ODD_DMA_START); + iowrite32(pd->width, pd->regs + EVEN_DMA_STRIDE); + iowrite32(pd->width, pd->regs + ODD_DMA_STRIDE); + /* enable interrupts, clear all irq flags */ + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | + FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD, + pd->regs + CSR1); + wait_i2c_reg(pd->regs); + write_i2c_reg(pd->regs, CONFIG, pd->config); + write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); + write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); + + /* start the board */ + write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD); + return 0; +} + +static void dt3155_stop_streaming(struct vb2_queue *q) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + struct vb2_buffer *vb; + + spin_lock_irq(&pd->lock); + /* stop the board */ + write_i2c_reg_nowait(pd->regs, CSR2, pd->csr2); + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1); + /* disable interrupts, clear all irq flags */ + iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); + spin_unlock_irq(&pd->lock); + + /* + * It is not clear whether the DMA stops at once or whether it + * will finish the current frame or field first. To be on the + * safe side we wait a bit. + */ + msleep(45); + + spin_lock_irq(&pd->lock); + if (pd->curr_buf) { + vb2_buffer_done(&pd->curr_buf->vb2_buf, VB2_BUF_STATE_ERROR); + pd->curr_buf = NULL; + } + + while (!list_empty(&pd->dmaq)) { + vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry); + list_del(&vb->done_entry); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irq(&pd->lock); +} + +static void dt3155_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); + + /* pd->vidq.streaming = 1 when dt3155_buf_queue() is invoked */ + spin_lock_irq(&pd->lock); + if (pd->curr_buf) + list_add_tail(&vb->done_entry, &pd->dmaq); + else + pd->curr_buf = vbuf; + spin_unlock_irq(&pd->lock); +} + +static const struct vb2_ops q_ops = { + .queue_setup = dt3155_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = dt3155_buf_prepare, + .start_streaming = dt3155_start_streaming, + .stop_streaming = dt3155_stop_streaming, + .buf_queue = dt3155_buf_queue, +}; + +static irqreturn_t dt3155_irq_handler_even(int irq, void *dev_id) +{ + struct dt3155_priv *ipd = dev_id; + struct vb2_buffer *ivb; + dma_addr_t dma_addr; + u32 tmp; + + tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD); + if (!tmp) + return IRQ_NONE; /* not our irq */ + if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) { + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START, + ipd->regs + INT_CSR); + return IRQ_HANDLED; /* start of field irq */ + } + tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD); + if (tmp) { + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN | + CAP_CONT_EVEN | CAP_CONT_ODD, + ipd->regs + CSR1); + mmiowb(); + } + + spin_lock(&ipd->lock); + if (ipd->curr_buf && !list_empty(&ipd->dmaq)) { + v4l2_get_timestamp(&ipd->curr_buf->timestamp); + ipd->curr_buf->sequence = ipd->sequence++; + ipd->curr_buf->field = V4L2_FIELD_NONE; + vb2_buffer_done(&ipd->curr_buf->vb2_buf, VB2_BUF_STATE_DONE); + + ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry); + list_del(&ivb->done_entry); + ipd->curr_buf = to_vb2_v4l2_buffer(ivb); + dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0); + iowrite32(dma_addr, ipd->regs + EVEN_DMA_START); + iowrite32(dma_addr + ipd->width, ipd->regs + ODD_DMA_START); + iowrite32(ipd->width, ipd->regs + EVEN_DMA_STRIDE); + iowrite32(ipd->width, ipd->regs + ODD_DMA_STRIDE); + mmiowb(); + } + + /* enable interrupts, clear all irq flags */ + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | + FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); + spin_unlock(&ipd->lock); + return IRQ_HANDLED; +} + +static const struct v4l2_file_operations dt3155_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll +}; + +static int dt3155_querycap(struct file *filp, void *p, + struct v4l2_capability *cap) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + strcpy(cap->driver, DT3155_NAME); + strcpy(cap->card, DT3155_NAME " frame grabber"); + sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int dt3155_enum_fmt_vid_cap(struct file *filp, + void *p, struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description, "8-bit Greyscale"); + return 0; +} + +static int dt3155_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + f->fmt.pix.width = pd->width; + f->fmt.pix.height = pd->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int dt3155_g_std(struct file *filp, void *p, v4l2_std_id *norm) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + *norm = pd->std; + return 0; +} + +static int dt3155_s_std(struct file *filp, void *p, v4l2_std_id norm) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + if (pd->std == norm) + return 0; + if (vb2_is_busy(&pd->vidq)) + return -EBUSY; + pd->std = norm; + if (pd->std & V4L2_STD_525_60) { + pd->csr2 = VT_60HZ; + pd->width = 640; + pd->height = 480; + } else { + pd->csr2 = VT_50HZ; + pd->width = 768; + pd->height = 576; + } + return 0; +} + +static int dt3155_enum_input(struct file *filp, void *p, + struct v4l2_input *input) +{ + if (input->index > 3) + return -EINVAL; + if (input->index) + snprintf(input->name, sizeof(input->name), "VID%d", + input->index); + else + strlcpy(input->name, "J2/VID0", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = V4L2_STD_ALL; + input->status = 0; + return 0; +} + +static int dt3155_g_input(struct file *filp, void *p, unsigned int *i) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + *i = pd->input; + return 0; +} + +static int dt3155_s_input(struct file *filp, void *p, unsigned int i) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + if (i > 3) + return -EINVAL; + pd->input = i; + write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); + write_i2c_reg(pd->regs, AD_CMD, (i << 6) | (i << 4) | SYNC_LVL_3); + return 0; +} + +static const struct v4l2_ioctl_ops dt3155_ioctl_ops = { + .vidioc_querycap = dt3155_querycap, + .vidioc_enum_fmt_vid_cap = dt3155_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = dt3155_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = dt3155_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = dt3155_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_std = dt3155_g_std, + .vidioc_s_std = dt3155_s_std, + .vidioc_enum_input = dt3155_enum_input, + .vidioc_g_input = dt3155_g_input, + .vidioc_s_input = dt3155_s_input, +}; + +static int dt3155_init_board(struct dt3155_priv *pd) +{ + struct pci_dev *pdev = pd->pdev; + int i; + u8 tmp = 0; + + pci_set_master(pdev); /* dt3155 needs it */ + + /* resetting the adapter */ + iowrite32(ADDR_ERR_ODD | ADDR_ERR_EVEN | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1); + mmiowb(); + msleep(20); + + /* initializing adapter registers */ + iowrite32(FIFO_EN | SRST, pd->regs + CSR1); + mmiowb(); + iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); + iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); + iowrite32(0x00000020, pd->regs + FIFO_TRIGER); + iowrite32(0x00000103, pd->regs + XFER_MODE); + iowrite32(0, pd->regs + RETRY_WAIT_CNT); + iowrite32(0, pd->regs + INT_CSR); + iowrite32(1, pd->regs + EVEN_FLD_MASK); + iowrite32(1, pd->regs + ODD_FLD_MASK); + iowrite32(0, pd->regs + MASK_LENGTH); + iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT); + iowrite32(0x01010101, pd->regs + IIC_CLK_DUR); + mmiowb(); + + /* verifying that we have a DT3155 board (not just a SAA7116 chip) */ + read_i2c_reg(pd->regs, DT_ID, &tmp); + if (tmp != DT3155_ID) + return -ENODEV; + + /* initialize AD LUT */ + write_i2c_reg(pd->regs, AD_ADDR, 0); + for (i = 0; i < 256; i++) + write_i2c_reg(pd->regs, AD_LUT, i); + + /* initialize ADC references */ + /* FIXME: pos_ref & neg_ref depend on VT_50HZ */ + write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); + write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); + write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF); + write_i2c_reg(pd->regs, AD_CMD, 34); + write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF); + write_i2c_reg(pd->regs, AD_CMD, 0); + + /* initialize PM LUT */ + write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM); + for (i = 0; i < 256; i++) { + write_i2c_reg(pd->regs, PM_LUT_ADDR, i); + write_i2c_reg(pd->regs, PM_LUT_DATA, i); + } + write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL); + for (i = 0; i < 256; i++) { + write_i2c_reg(pd->regs, PM_LUT_ADDR, i); + write_i2c_reg(pd->regs, PM_LUT_DATA, i); + } + write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ + + /* select channel 1 for input and set sync level */ + write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); + write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); + + /* disable all irqs, clear all irq flags */ + iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, + pd->regs + INT_CSR); + + return 0; +} + +static struct video_device dt3155_vdev = { + .name = DT3155_NAME, + .fops = &dt3155_fops, + .ioctl_ops = &dt3155_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .tvnorms = V4L2_STD_ALL, +}; + +static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct dt3155_priv *pd; + + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + return -ENODEV; + pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + err = v4l2_device_register(&pdev->dev, &pd->v4l2_dev); + if (err) + return err; + pd->vdev = dt3155_vdev; + pd->vdev.v4l2_dev = &pd->v4l2_dev; + video_set_drvdata(&pd->vdev, pd); /* for use in video_fops */ + pd->pdev = pdev; + pd->std = V4L2_STD_625_50; + pd->csr2 = VT_50HZ; + pd->width = 768; + pd->height = 576; + INIT_LIST_HEAD(&pd->dmaq); + mutex_init(&pd->mux); + pd->vdev.lock = &pd->mux; /* for locking v4l2_file_operations */ + pd->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pd->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + pd->vidq.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + pd->vidq.ops = &q_ops; + pd->vidq.mem_ops = &vb2_dma_contig_memops; + pd->vidq.drv_priv = pd; + pd->vidq.min_buffers_needed = 2; + pd->vidq.gfp_flags = GFP_DMA32; + pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */ + pd->vdev.queue = &pd->vidq; + err = vb2_queue_init(&pd->vidq); + if (err < 0) + goto err_v4l2_dev_unreg; + pd->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(pd->alloc_ctx)) { + dev_err(&pdev->dev, "Can't allocate buffer context"); + err = PTR_ERR(pd->alloc_ctx); + goto err_v4l2_dev_unreg; + } + spin_lock_init(&pd->lock); + pd->config = ACQ_MODE_EVEN; + err = pci_enable_device(pdev); + if (err) + goto err_free_ctx; + err = pci_request_region(pdev, 0, pci_name(pdev)); + if (err) + goto err_pci_disable; + pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0)); + if (!pd->regs) { + err = -ENOMEM; + goto err_free_reg; + } + err = dt3155_init_board(pd); + if (err) + goto err_iounmap; + err = request_irq(pd->pdev->irq, dt3155_irq_handler_even, + IRQF_SHARED, DT3155_NAME, pd); + if (err) + goto err_iounmap; + err = video_register_device(&pd->vdev, VFL_TYPE_GRABBER, -1); + if (err) + goto err_free_irq; + dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor); + return 0; /* success */ + +err_free_irq: + free_irq(pd->pdev->irq, pd); +err_iounmap: + pci_iounmap(pdev, pd->regs); +err_free_reg: + pci_release_region(pdev, 0); +err_pci_disable: + pci_disable_device(pdev); +err_free_ctx: + vb2_dma_contig_cleanup_ctx(pd->alloc_ctx); +err_v4l2_dev_unreg: + v4l2_device_unregister(&pd->v4l2_dev); + return err; +} + +static void dt3155_remove(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct dt3155_priv *pd = container_of(v4l2_dev, struct dt3155_priv, + v4l2_dev); + + video_unregister_device(&pd->vdev); + free_irq(pd->pdev->irq, pd); + vb2_queue_release(&pd->vidq); + v4l2_device_unregister(&pd->v4l2_dev); + pci_iounmap(pdev, pd->regs); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + vb2_dma_contig_cleanup_ctx(pd->alloc_ctx); +} + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) }, + { 0, /* zero marks the end */ }, +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver pci_driver = { + .name = DT3155_NAME, + .id_table = pci_ids, + .probe = dt3155_probe, + .remove = dt3155_remove, +}; + +module_pci_driver(pci_driver); + +MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber"); +MODULE_AUTHOR("Marin Mitov "); +MODULE_VERSION(DT3155_VERSION); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/pci/dt3155/dt3155.h b/kernel/drivers/media/pci/dt3155/dt3155.h new file mode 100644 index 000000000..b3531e0bc --- /dev/null +++ b/kernel/drivers/media/pci/dt3155/dt3155.h @@ -0,0 +1,197 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Marin Mitov * + * mitov@issp.bas.bg * + * * + * 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. * + * * + ***************************************************************************/ + +/* DT3155 header file */ +#ifndef _DT3155_H_ +#define _DT3155_H_ + +#include +#include +#include +#include +#include + +#define DT3155_NAME "dt3155" +#define DT3155_VER_MAJ 2 +#define DT3155_VER_MIN 0 +#define DT3155_VER_EXT 0 +#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \ + __stringify(DT3155_VER_MIN) "." \ + __stringify(DT3155_VER_EXT) + +/* DT3155 Base Register offsets (memory mapped) */ +#define EVEN_DMA_START 0x00 +#define ODD_DMA_START 0x0C +#define EVEN_DMA_STRIDE 0x18 +#define ODD_DMA_STRIDE 0x24 +#define EVEN_PIXEL_FMT 0x30 +#define ODD_PIXEL_FMT 0x34 +#define FIFO_TRIGER 0x38 +#define XFER_MODE 0x3C +#define CSR1 0x40 +#define RETRY_WAIT_CNT 0x44 +#define INT_CSR 0x48 +#define EVEN_FLD_MASK 0x4C +#define ODD_FLD_MASK 0x50 +#define MASK_LENGTH 0x54 +#define FIFO_FLAG_CNT 0x58 +#define IIC_CLK_DUR 0x5C +#define IIC_CSR1 0x60 +#define IIC_CSR2 0x64 + +/* DT3155 Internal Registers indexes (i2c/IIC mapped) */ +#define CSR2 0x10 +#define EVEN_CSR 0x11 +#define ODD_CSR 0x12 +#define CONFIG 0x13 +#define DT_ID 0x1F +#define X_CLIP_START 0x20 +#define Y_CLIP_START 0x22 +#define X_CLIP_END 0x24 +#define Y_CLIP_END 0x26 +#define AD_ADDR 0x30 +#define AD_LUT 0x31 +#define AD_CMD 0x32 +#define DIG_OUT 0x40 +#define PM_LUT_ADDR 0x50 +#define PM_LUT_DATA 0x51 + +/* AD command register values */ +#define AD_CMD_REG 0x00 +#define AD_POS_REF 0x01 +#define AD_NEG_REF 0x02 + +/* CSR1 bit masks */ +#define RANGE_EN 0x00008000 +#define CRPT_DIS 0x00004000 +#define ADDR_ERR_ODD 0x00000800 +#define ADDR_ERR_EVEN 0x00000400 +#define FLD_CRPT_ODD 0x00000200 +#define FLD_CRPT_EVEN 0x00000100 +#define FIFO_EN 0x00000080 +#define SRST 0x00000040 +#define FLD_DN_ODD 0x00000020 +#define FLD_DN_EVEN 0x00000010 +/* These should not be used. + * Use CAP_CONT_ODD/EVEN instead +#define CAP_SNGL_ODD 0x00000008 +#define CAP_SNGL_EVEN 0x00000004 +*/ +#define CAP_CONT_ODD 0x00000002 +#define CAP_CONT_EVEN 0x00000001 + +/* INT_CSR bit masks */ +#define FLD_START_EN 0x00000400 +#define FLD_END_ODD_EN 0x00000200 +#define FLD_END_EVEN_EN 0x00000100 +#define FLD_START 0x00000004 +#define FLD_END_ODD 0x00000002 +#define FLD_END_EVEN 0x00000001 + +/* IIC_CSR1 bit masks */ +#define DIRECT_ABORT 0x00000200 + +/* IIC_CSR2 bit masks */ +#define NEW_CYCLE 0x01000000 +#define DIR_RD 0x00010000 +#define IIC_READ 0x01010000 +#define IIC_WRITE 0x01000000 + +/* CSR2 bit masks */ +#define DISP_PASS 0x40 +#define BUSY_ODD 0x20 +#define BUSY_EVEN 0x10 +#define SYNC_PRESENT 0x08 +#define VT_50HZ 0x04 +#define SYNC_SNTL 0x02 +#define CHROM_FILT 0x01 +#define VT_60HZ 0x00 + +/* CSR_EVEN/ODD bit masks */ +#define CSR_ERROR 0x04 +#define CSR_SNGL 0x02 +#define CSR_DONE 0x01 + +/* CONFIG bit masks */ +#define PM_LUT_PGM 0x80 +#define PM_LUT_SEL 0x40 +#define CLIP_EN 0x20 +#define HSCALE_EN 0x10 +#define EXT_TRIG_UP 0x0C +#define EXT_TRIG_DOWN 0x04 +#define ACQ_MODE_NEXT 0x02 +#define ACQ_MODE_ODD 0x01 +#define ACQ_MODE_EVEN 0x00 + +/* AD_CMD bit masks */ +#define VIDEO_CNL_1 0x00 +#define VIDEO_CNL_2 0x40 +#define VIDEO_CNL_3 0x80 +#define VIDEO_CNL_4 0xC0 +#define SYNC_CNL_1 0x00 +#define SYNC_CNL_2 0x10 +#define SYNC_CNL_3 0x20 +#define SYNC_CNL_4 0x30 +#define SYNC_LVL_1 0x00 +#define SYNC_LVL_2 0x04 +#define SYNC_LVL_3 0x08 +#define SYNC_LVL_4 0x0C + +/* DT3155 identificator */ +#define DT3155_ID 0x20 + +/* per board private data structure */ +/** + * struct dt3155_priv - private data structure + * + * @v4l2_dev: v4l2_device structure + * @vdev: video_device structure + * @pdev: pointer to pci_dev structure + * @vidq: vb2_queue structure + * @alloc_ctx: dma_contig allocation context + * @curr_buf: pointer to curren buffer + * @mux: mutex to protect the instance + * @dmaq: queue for dma buffers + * @lock: spinlock for dma queue + * @std: input standard + * @width: frame width + * @height: frame height + * @input: current input + * @sequence: frame counter + * @stats: statistics structure + * @regs: local copy of mmio base register + * @csr2: local copy of csr2 register + * @config: local copy of config register + */ +struct dt3155_priv { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct pci_dev *pdev; + struct vb2_queue vidq; + struct vb2_alloc_ctx *alloc_ctx; + struct vb2_v4l2_buffer *curr_buf; + struct mutex mux; + struct list_head dmaq; + spinlock_t lock; + v4l2_std_id std; + unsigned width, height; + unsigned input; + unsigned int sequence; + void __iomem *regs; + u8 csr2, config; +}; + +#endif /* _DT3155_H_ */ diff --git a/kernel/drivers/media/pci/ivtv/Kconfig b/kernel/drivers/media/pci/ivtv/Kconfig index dd6ee57e3..6e5867c57 100644 --- a/kernel/drivers/media/pci/ivtv/Kconfig +++ b/kernel/drivers/media/pci/ivtv/Kconfig @@ -57,5 +57,8 @@ config VIDEO_FB_IVTV This is used in the Hauppauge PVR-350 card. There is a driver homepage at . + In order to use this module, you will need to boot with PAT disabled + on x86 systems, using the nopat kernel parameter. + To compile this driver as a module, choose M here: the module will be called ivtvfb. diff --git a/kernel/drivers/media/pci/ivtv/ivtv-alsa-main.c b/kernel/drivers/media/pci/ivtv/ivtv-alsa-main.c index 41fa21534..8a86b61a8 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-alsa-main.c +++ b/kernel/drivers/media/pci/ivtv/ivtv-alsa-main.c @@ -41,6 +41,7 @@ #include "ivtv-alsa-pcm.h" int ivtv_alsa_debug; +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; #define IVTV_DEBUG_ALSA_INFO(fmt, arg...) \ do { \ @@ -54,6 +55,10 @@ MODULE_PARM_DESC(debug, "\t\t\t 1/0x0001: warning\n" "\t\t\t 2/0x0002: info\n"); +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, + "Index value for IVTV ALSA capture interface(s).\n"); + MODULE_AUTHOR("Andy Walls"); MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface"); MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder"); @@ -137,7 +142,7 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev) struct ivtv *itv = to_ivtv(v4l2_dev); struct snd_card *sc = NULL; struct snd_ivtv_card *itvsc; - int ret; + int ret, idx; /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ @@ -145,8 +150,10 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev) /* This is a no-op for us. We'll use the itv->instance */ /* (2) Create a card instance */ + /* use first available id if not specified otherwise*/ + idx = index[itv->instance] == -1 ? SNDRV_DEFAULT_IDX1 : index[itv->instance]; ret = snd_card_new(&itv->pdev->dev, - SNDRV_DEFAULT_IDX1, /* use first available id */ + idx, SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ THIS_MODULE, 0, &sc); if (ret) { @@ -196,6 +203,9 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev) goto err_exit_free; } + IVTV_ALSA_INFO("%s: Instance %d registered as ALSA card %d\n", + __func__, itv->instance, sc->number); + return 0; err_exit_free: diff --git a/kernel/drivers/media/pci/ivtv/ivtv-controls.c b/kernel/drivers/media/pci/ivtv/ivtv-controls.c index ccf548c25..8a55ccb8f 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-controls.c +++ b/kernel/drivers/media/pci/ivtv/ivtv-controls.c @@ -64,13 +64,15 @@ static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) { struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; /* fix videodecoder resolution */ - fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); - fmt.height = cxhdl->height; - fmt.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt); + format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1); + format.format.height = cxhdl->height; + format.format.code = MEDIA_BUS_FMT_FIXED; + v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format); return 0; } diff --git a/kernel/drivers/media/pci/ivtv/ivtv-driver.h b/kernel/drivers/media/pci/ivtv/ivtv-driver.h index e8b6c7ad2..ee0ef6e48 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-driver.h +++ b/kernel/drivers/media/pci/ivtv/ivtv-driver.h @@ -830,7 +830,8 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) do { \ struct v4l2_subdev *__sd; \ __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \ - !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ + !(hw) ? true : (__sd->grp_id & (hw)), \ + o, f, ##args); \ } while (0) #define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) diff --git a/kernel/drivers/media/pci/ivtv/ivtv-gpio.c b/kernel/drivers/media/pci/ivtv/ivtv-gpio.c index af52def70..f752f3993 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-gpio.c +++ b/kernel/drivers/media/pci/ivtv/ivtv-gpio.c @@ -313,13 +313,6 @@ static const struct v4l2_ctrl_ops gpio_ctrl_ops = { static const struct v4l2_subdev_core_ops subdev_core_ops = { .log_status = subdev_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { diff --git a/kernel/drivers/media/pci/ivtv/ivtv-ioctl.c b/kernel/drivers/media/pci/ivtv/ivtv-ioctl.c index 6fe6c4a0e..9a21c17fc 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/kernel/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -581,7 +581,9 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f { struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; @@ -599,10 +601,10 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f itv->cxhdl.height = h; if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) fmt->fmt.pix.width /= 2; - mbus_fmt.width = fmt->fmt.pix.width; - mbus_fmt.height = h; - mbus_fmt.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt); + format.format.width = fmt->fmt.pix.width; + format.format.height = h; + format.format.code = MEDIA_BUS_FMT_FIXED; + v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format); return ivtv_g_fmt_vid_cap(file, fh, fmt); } @@ -1529,7 +1531,8 @@ static int ivtv_log_status(struct file *file, void *fh) ivtv_get_audio_input(itv, itv->audio_input, &audin); IVTV_INFO("Video Input: %s\n", vidin.name); IVTV_INFO("Audio Input: %s%s\n", audin.name, - (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : ""); + itv->dualwatch_stereo_mode == V4L2_MPEG_AUDIO_MODE_DUAL ? + " (Bilingual)" : ""); if (has_output) { struct v4l2_output vidout; struct v4l2_audioout audout; diff --git a/kernel/drivers/media/pci/ivtv/ivtv-yuv.c b/kernel/drivers/media/pci/ivtv/ivtv-yuv.c index 2ad65eb29..2b8e7b2f2 100644 --- a/kernel/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/kernel/drivers/media/pci/ivtv/ivtv-yuv.c @@ -75,15 +75,15 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); /* Get user pages for DMA Xfer */ - down_read(¤t->mm->mmap_sem); - y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + y_pages = get_user_pages_unlocked(current, current->mm, + y_dma.uaddr, y_dma.page_count, 0, 1, + &dma->map[0]); uv_pages = 0; /* silence gcc. value is set and consumed only if: */ if (y_pages == y_dma.page_count) { - uv_pages = get_user_pages(current, current->mm, - uv_dma.uaddr, uv_dma.page_count, 0, 1, - &dma->map[y_pages], NULL); + uv_pages = get_user_pages_unlocked(current, current->mm, + uv_dma.uaddr, uv_dma.page_count, 0, 1, + &dma->map[y_pages]); } - up_read(¤t->mm->mmap_sem); if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { int rc = -EFAULT; diff --git a/kernel/drivers/media/pci/ivtv/ivtvfb.c b/kernel/drivers/media/pci/ivtv/ivtvfb.c index 9ff123019..8b95eefb6 100644 --- a/kernel/drivers/media/pci/ivtv/ivtvfb.c +++ b/kernel/drivers/media/pci/ivtv/ivtvfb.c @@ -38,14 +38,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include #include #include -#ifdef CONFIG_MTRR -#include +#ifdef CONFIG_X86_64 +#include #endif #include "ivtv-driver.h" @@ -155,12 +157,11 @@ struct osd_info { /* Buffer size */ u32 video_buffer_size; -#ifdef CONFIG_MTRR /* video_base rounded down as required by hardware MTRRs */ unsigned long fb_start_aligned_physaddr; /* video_base rounded up as required by hardware MTRRs */ unsigned long fb_end_aligned_physaddr; -#endif + int wc_cookie; /* Store the buffer offset */ int set_osd_coords_x; @@ -1099,6 +1100,8 @@ static int ivtvfb_init_vidmode(struct ivtv *itv) static int ivtvfb_init_io(struct ivtv *itv) { struct osd_info *oi = itv->osd_info; + /* Find the largest power of two that maps the whole buffer */ + int size_shift = 31; mutex_lock(&itv->serialize_lock); if (ivtv_init_on_first_open(itv)) { @@ -1132,29 +1135,16 @@ static int ivtvfb_init_io(struct ivtv *itv) oi->video_pbase, oi->video_vbase, oi->video_buffer_size / 1024); -#ifdef CONFIG_MTRR - { - /* Find the largest power of two that maps the whole buffer */ - int size_shift = 31; - - while (!(oi->video_buffer_size & (1 << size_shift))) { - size_shift--; - } - size_shift++; - oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); - oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; - oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; - oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); - if (mtrr_add(oi->fb_start_aligned_physaddr, - oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr, - MTRR_TYPE_WRCOMB, 1) < 0) { - IVTVFB_INFO("disabled mttr\n"); - oi->fb_start_aligned_physaddr = 0; - oi->fb_end_aligned_physaddr = 0; - } - } -#endif - + while (!(oi->video_buffer_size & (1 << size_shift))) + size_shift--; + size_shift++; + oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); + oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; + oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; + oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); + oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr, + oi->fb_end_aligned_physaddr - + oi->fb_start_aligned_physaddr); /* Blank the entire osd. */ memset_io(oi->video_vbase, 0, oi->video_buffer_size); @@ -1172,14 +1162,7 @@ static void ivtvfb_release_buffers (struct ivtv *itv) /* Release pseudo palette */ kfree(oi->ivtvfb_info.pseudo_palette); - -#ifdef CONFIG_MTRR - if (oi->fb_end_aligned_physaddr) { - mtrr_del(-1, oi->fb_start_aligned_physaddr, - oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr); - } -#endif - + arch_phys_wc_del(oi->wc_cookie); kfree(oi); itv->osd_info = NULL; } @@ -1190,6 +1173,13 @@ static int ivtvfb_init_card(struct ivtv *itv) { int rc; +#ifdef CONFIG_X86_64 + if (pat_enabled()) { + pr_warn("ivtvfb needs PAT disabled, boot with nopat kernel parameter\n"); + return -ENODEV; + } +#endif + if (itv->osd_info) { IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); return -EBUSY; @@ -1284,6 +1274,7 @@ static int __init ivtvfb_init(void) int registered = 0; int err; + if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", IVTV_MAX_CARDS - 1); diff --git a/kernel/drivers/media/pci/mantis/hopper_cards.c b/kernel/drivers/media/pci/mantis/hopper_cards.c index 104914a5b..68b580003 100644 --- a/kernel/drivers/media/pci/mantis/hopper_cards.c +++ b/kernel/drivers/media/pci/mantis/hopper_cards.c @@ -106,6 +106,10 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) } if (stat & MANTIS_INT_IRQ1) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); + spin_lock(&mantis->intmask_lock); + mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ1, + MANTIS_INT_MASK); + spin_unlock(&mantis->intmask_lock); schedule_work(&mantis->uart_work); } if (stat & MANTIS_INT_OCERR) { @@ -154,6 +158,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) static int hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { + struct mantis_pci_drvdata *drvdata; struct mantis_pci *mantis; struct mantis_hwconfig *config; int err = 0; @@ -165,12 +170,16 @@ static int hopper_pci_probe(struct pci_dev *pdev, goto fail0; } + drvdata = (void *)pci_id->driver_data; mantis->num = devs; mantis->verbose = verbose; mantis->pdev = pdev; - config = (struct mantis_hwconfig *) pci_id->driver_data; + config = drvdata->hwconfig; config->irq_handler = &hopper_irq_handler; mantis->hwconfig = config; + mantis->rc_map_name = drvdata->rc_map_name; + + spin_lock_init(&mantis->intmask_lock); err = mantis_pci_init(mantis); if (err) { @@ -247,7 +256,8 @@ static void hopper_pci_remove(struct pci_dev *pdev) } static struct pci_device_id hopper_pci_table[] = { - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config, + NULL), { } }; diff --git a/kernel/drivers/media/pci/mantis/mantis_cards.c b/kernel/drivers/media/pci/mantis/mantis_cards.c index 801fc55b6..cdefffc16 100644 --- a/kernel/drivers/media/pci/mantis/mantis_cards.c +++ b/kernel/drivers/media/pci/mantis/mantis_cards.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "dmxdev.h" #include "dvbdev.h" @@ -49,6 +50,7 @@ #include "mantis_pci.h" #include "mantis_i2c.h" #include "mantis_reg.h" +#include "mantis_input.h" static unsigned int verbose; module_param(verbose, int, 0644); @@ -114,6 +116,10 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) } if (stat & MANTIS_INT_IRQ1) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); + spin_lock(&mantis->intmask_lock); + mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ1, + MANTIS_INT_MASK); + spin_unlock(&mantis->intmask_lock); schedule_work(&mantis->uart_work); } if (stat & MANTIS_INT_OCERR) { @@ -162,6 +168,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) static int mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { + struct mantis_pci_drvdata *drvdata; struct mantis_pci *mantis; struct mantis_hwconfig *config; int err = 0; @@ -169,84 +176,91 @@ static int mantis_pci_probe(struct pci_dev *pdev, mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); if (mantis == NULL) { printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); - err = -ENOMEM; - goto fail0; + return -ENOMEM; } + drvdata = (void *)pci_id->driver_data; mantis->num = devs; mantis->verbose = verbose; mantis->pdev = pdev; - config = (struct mantis_hwconfig *) pci_id->driver_data; + config = drvdata->hwconfig; config->irq_handler = &mantis_irq_handler; mantis->hwconfig = config; + mantis->rc_map_name = drvdata->rc_map_name; + + spin_lock_init(&mantis->intmask_lock); err = mantis_pci_init(mantis); if (err) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); - goto fail1; + goto err_free_mantis; } err = mantis_stream_control(mantis, STREAM_TO_HIF); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); - goto fail1; + goto err_pci_exit; } err = mantis_i2c_init(mantis); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); - goto fail2; + goto err_pci_exit; } err = mantis_get_mac(mantis); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); - goto fail2; + goto err_i2c_exit; } err = mantis_dma_init(mantis); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); - goto fail3; + goto err_i2c_exit; } err = mantis_dvb_init(mantis); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); - goto fail4; + goto err_dma_exit; + } + + err = mantis_input_init(mantis); + if (err < 0) { + dprintk(MANTIS_ERROR, 1, + "ERROR: Mantis DVB initialization failed <%d>", err); + goto err_dvb_exit; } + err = mantis_uart_init(mantis); if (err < 0) { dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err); - goto fail6; + goto err_input_exit; } devs++; - return err; + return 0; +err_input_exit: + mantis_input_exit(mantis); - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err); - mantis_uart_exit(mantis); +err_dvb_exit: + mantis_dvb_exit(mantis); -fail6: -fail4: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); +err_dma_exit: mantis_dma_exit(mantis); -fail3: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); +err_i2c_exit: mantis_i2c_exit(mantis); -fail2: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); +err_pci_exit: mantis_pci_exit(mantis); -fail1: - dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); +err_free_mantis: kfree(mantis); -fail0: return err; } @@ -257,6 +271,7 @@ static void mantis_pci_remove(struct pci_dev *pdev) if (mantis) { mantis_uart_exit(mantis); + mantis_input_exit(mantis); mantis_dvb_exit(mantis); mantis_dma_exit(mantis); mantis_i2c_exit(mantis); @@ -267,17 +282,28 @@ static void mantis_pci_remove(struct pci_dev *pdev) } static struct pci_device_id mantis_pci_table[] = { - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config), - MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config), - MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config), - MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config), - MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config), - MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config), - MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config), + MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config, + RC_MAP_TECHNISAT_TS35), + MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config, + NULL), + MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config, + NULL), + MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config, + RC_MAP_TERRATEC_CINERGY_C_PCI), + MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config, + RC_MAP_TERRATEC_CINERGY_S2_HD), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config, + NULL), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config, + NULL), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config, + RC_MAP_TWINHAN_DTV_CAB_CI), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config, + RC_MAP_TWINHAN_DTV_CAB_CI), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config, + NULL), + MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config, + NULL), { } }; diff --git a/kernel/drivers/media/pci/mantis/mantis_common.h b/kernel/drivers/media/pci/mantis/mantis_common.h index 8ff448bb7..d48778a36 100644 --- a/kernel/drivers/media/pci/mantis/mantis_common.h +++ b/kernel/drivers/media/pci/mantis/mantis_common.h @@ -25,6 +25,7 @@ #include #include +#include "mantis_reg.h" #include "mantis_uart.h" #include "mantis_link.h" @@ -68,12 +69,13 @@ #define TECHNISAT 0x1ae4 #define TERRATEC 0x153b -#define MAKE_ENTRY(__subven, __subdev, __configptr) { \ +#define MAKE_ENTRY(__subven, __subdev, __configptr, __rc) { \ .vendor = TWINHAN_TECHNOLOGIES, \ .device = MANTIS, \ .subvendor = (__subven), \ .subdevice = (__subdev), \ - .driver_data = (unsigned long) (__configptr) \ + .driver_data = (unsigned long) \ + &(struct mantis_pci_drvdata){__configptr, __rc} \ } enum mantis_i2c_mode { @@ -101,6 +103,11 @@ struct mantis_hwconfig { enum mantis_i2c_mode i2c_mode; }; +struct mantis_pci_drvdata { + struct mantis_hwconfig *hwconfig; + char *rc_map_name; +}; + struct mantis_pci { unsigned int verbose; @@ -131,6 +138,7 @@ struct mantis_pci { dma_addr_t risc_dma; struct tasklet_struct tasklet; + spinlock_t intmask_lock; struct i2c_adapter adapter; int i2c_rc; @@ -165,15 +173,32 @@ struct mantis_pci { struct mantis_ca *mantis_ca; - wait_queue_head_t uart_wq; struct work_struct uart_work; - spinlock_t uart_lock; struct rc_dev *rc; char input_name[80]; char input_phys[80]; + char *rc_map_name; }; #define MANTIS_HIF_STATUS (mantis->gpio_status) +static inline void mantis_mask_ints(struct mantis_pci *mantis, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&mantis->intmask_lock, flags); + mmwrite(mmread(MANTIS_INT_MASK) & ~mask, MANTIS_INT_MASK); + spin_unlock_irqrestore(&mantis->intmask_lock, flags); +} + +static inline void mantis_unmask_ints(struct mantis_pci *mantis, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&mantis->intmask_lock, flags); + mmwrite(mmread(MANTIS_INT_MASK) | mask, MANTIS_INT_MASK); + spin_unlock_irqrestore(&mantis->intmask_lock, flags); +} + #endif /* __MANTIS_COMMON_H */ diff --git a/kernel/drivers/media/pci/mantis/mantis_dma.c b/kernel/drivers/media/pci/mantis/mantis_dma.c index 566c40717..2ce310b0a 100644 --- a/kernel/drivers/media/pci/mantis/mantis_dma.c +++ b/kernel/drivers/media/pci/mantis/mantis_dma.c @@ -130,21 +130,20 @@ err: int mantis_dma_init(struct mantis_pci *mantis) { - int err = 0; + int err; dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); - if (mantis_alloc_buffers(mantis) < 0) { + err = mantis_alloc_buffers(mantis); + if (err < 0) { dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); /* Stop RISC Engine */ mmwrite(0, MANTIS_DMA_CTL); - goto err; + return err; } return 0; -err: - return err; } EXPORT_SYMBOL_GPL(mantis_dma_init); @@ -190,7 +189,7 @@ void mantis_dma_start(struct mantis_pci *mantis) mmwrite(0, MANTIS_DMA_CTL); mantis->last_block = mantis->busy_block = 0; - mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK); + mantis_unmask_ints(mantis, MANTIS_INT_RISCI); mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN | MANTIS_RISC_EN, MANTIS_DMA_CTL); @@ -209,8 +208,7 @@ void mantis_dma_stop(struct mantis_pci *mantis) mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT); - mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI | - MANTIS_INT_RISCEN), MANTIS_INT_MASK); + mantis_mask_ints(mantis, MANTIS_INT_RISCI | MANTIS_INT_RISCEN); } diff --git a/kernel/drivers/media/pci/mantis/mantis_i2c.c b/kernel/drivers/media/pci/mantis/mantis_i2c.c index 895ddba3c..d72ee47dc 100644 --- a/kernel/drivers/media/pci/mantis/mantis_i2c.c +++ b/kernel/drivers/media/pci/mantis/mantis_i2c.c @@ -219,7 +219,7 @@ static struct i2c_algorithm mantis_algo = { int mantis_i2c_init(struct mantis_pci *mantis) { - u32 intstat, intmask; + u32 intstat; struct i2c_adapter *i2c_adapter = &mantis->adapter; struct pci_dev *pdev = mantis->pdev; @@ -242,11 +242,10 @@ int mantis_i2c_init(struct mantis_pci *mantis) dprintk(MANTIS_DEBUG, 1, "Initializing I2C .."); intstat = mmread(MANTIS_INT_STAT); - intmask = mmread(MANTIS_INT_MASK); + mmread(MANTIS_INT_MASK); mmwrite(intstat, MANTIS_INT_STAT); dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); - intmask = mmread(MANTIS_INT_MASK); - mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); + mantis_mask_ints(mantis, MANTIS_INT_I2CDONE); return 0; } @@ -254,11 +253,8 @@ EXPORT_SYMBOL_GPL(mantis_i2c_init); int mantis_i2c_exit(struct mantis_pci *mantis) { - u32 intmask; - dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); - intmask = mmread(MANTIS_INT_MASK); - mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); + mantis_mask_ints(mantis, MANTIS_INT_I2CDONE); dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter"); i2c_del_adapter(&mantis->adapter); diff --git a/kernel/drivers/media/pci/mantis/mantis_input.c b/kernel/drivers/media/pci/mantis/mantis_input.c index 0e5252e5c..7f7f1d4d7 100644 --- a/kernel/drivers/media/pci/mantis/mantis_input.c +++ b/kernel/drivers/media/pci/mantis/mantis_input.c @@ -12,14 +12,8 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#if 0 /* Currently unused */ - #include #include @@ -30,100 +24,32 @@ #include "dvb_net.h" #include "mantis_common.h" -#include "mantis_reg.h" -#include "mantis_uart.h" +#include "mantis_input.h" #define MODULE_NAME "mantis_core" -#define RC_MAP_MANTIS "rc-mantis" - -static struct rc_map_table mantis_ir_table[] = { - { 0x29, KEY_POWER }, - { 0x28, KEY_FAVORITES }, - { 0x30, KEY_TEXT }, - { 0x17, KEY_INFO }, /* Preview */ - { 0x23, KEY_EPG }, - { 0x3b, KEY_F22 }, /* Record List */ - { 0x3c, KEY_1 }, - { 0x3e, KEY_2 }, - { 0x39, KEY_3 }, - { 0x36, KEY_4 }, - { 0x22, KEY_5 }, - { 0x20, KEY_6 }, - { 0x32, KEY_7 }, - { 0x26, KEY_8 }, - { 0x24, KEY_9 }, - { 0x2a, KEY_0 }, - - { 0x33, KEY_CANCEL }, - { 0x2c, KEY_BACK }, - { 0x15, KEY_CLEAR }, - { 0x3f, KEY_TAB }, - { 0x10, KEY_ENTER }, - { 0x14, KEY_UP }, - { 0x0d, KEY_RIGHT }, - { 0x0e, KEY_DOWN }, - { 0x11, KEY_LEFT }, - - { 0x21, KEY_VOLUMEUP }, - { 0x35, KEY_VOLUMEDOWN }, - { 0x3d, KEY_CHANNELDOWN }, - { 0x3a, KEY_CHANNELUP }, - { 0x2e, KEY_RECORD }, - { 0x2b, KEY_PLAY }, - { 0x13, KEY_PAUSE }, - { 0x25, KEY_STOP }, - - { 0x1f, KEY_REWIND }, - { 0x2d, KEY_FASTFORWARD }, - { 0x1e, KEY_PREVIOUS }, /* Replay |< */ - { 0x1d, KEY_NEXT }, /* Skip >| */ - - { 0x0b, KEY_CAMERA }, /* Capture */ - { 0x0f, KEY_LANGUAGE }, /* SAP */ - { 0x18, KEY_MODE }, /* PIP */ - { 0x12, KEY_ZOOM }, /* Full screen */ - { 0x1c, KEY_SUBTITLE }, - { 0x2f, KEY_MUTE }, - { 0x16, KEY_F20 }, /* L/R */ - { 0x38, KEY_F21 }, /* Hibernate */ - - { 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */ - { 0x31, KEY_AGAIN }, /* Recall */ - { 0x1a, KEY_KPPLUS }, /* Zoom+ */ - { 0x19, KEY_KPMINUS }, /* Zoom- */ - { 0x27, KEY_RED }, - { 0x0C, KEY_GREEN }, - { 0x01, KEY_YELLOW }, - { 0x00, KEY_BLUE }, -}; - -static struct rc_map_list ir_mantis_map = { - .map = { - .scan = mantis_ir_table, - .size = ARRAY_SIZE(mantis_ir_table), - .rc_type = RC_TYPE_UNKNOWN, - .name = RC_MAP_MANTIS, - } -}; + +void mantis_input_process(struct mantis_pci *mantis, int scancode) +{ + if (mantis->rc) + rc_keydown(mantis->rc, RC_TYPE_UNKNOWN, scancode, 0); +} int mantis_input_init(struct mantis_pci *mantis) { struct rc_dev *dev; int err; - err = rc_map_register(&ir_mantis_map); - if (err) - goto out; - dev = rc_allocate_device(); if (!dev) { dprintk(MANTIS_ERROR, 1, "Remote device allocation failed"); err = -ENOMEM; - goto out_map; + goto out; } - sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name); - sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev)); + snprintf(mantis->input_name, sizeof(mantis->input_name), + "Mantis %s IR receiver", mantis->hwconfig->model_name); + snprintf(mantis->input_phys, sizeof(mantis->input_phys), + "pci-%s/ir0", pci_name(mantis->pdev)); dev->input_name = mantis->input_name; dev->input_phys = mantis->input_phys; @@ -132,7 +58,7 @@ int mantis_input_init(struct mantis_pci *mantis) dev->input_id.product = mantis->device_id; dev->input_id.version = 1; dev->driver_name = MODULE_NAME; - dev->map_name = RC_MAP_MANTIS; + dev->map_name = mantis->rc_map_name ? : RC_MAP_EMPTY; dev->dev.parent = &mantis->pdev->dev; err = rc_register_device(dev); @@ -146,17 +72,13 @@ int mantis_input_init(struct mantis_pci *mantis) out_dev: rc_free_device(dev); -out_map: - rc_map_unregister(&ir_mantis_map); out: return err; } +EXPORT_SYMBOL_GPL(mantis_input_init); -int mantis_init_exit(struct mantis_pci *mantis) +void mantis_input_exit(struct mantis_pci *mantis) { rc_unregister_device(mantis->rc); - rc_map_unregister(&ir_mantis_map); - return 0; } - -#endif +EXPORT_SYMBOL_GPL(mantis_input_exit); diff --git a/kernel/drivers/media/pci/mantis/mantis_input.h b/kernel/drivers/media/pci/mantis/mantis_input.h new file mode 100644 index 000000000..0fbd92987 --- /dev/null +++ b/kernel/drivers/media/pci/mantis/mantis_input.h @@ -0,0 +1,24 @@ +/* + Mantis PCI bridge driver + + Copyright (C) Manu Abraham (abraham.manu@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 __MANTIS_INPUT_H +#define __MANTIS_INPUT_H + +int mantis_input_init(struct mantis_pci *mantis); +void mantis_input_exit(struct mantis_pci *mantis); +void mantis_input_process(struct mantis_pci *mantis, int scancode); + +#endif /* __MANTIS_UART_H */ diff --git a/kernel/drivers/media/pci/mantis/mantis_pcmcia.c b/kernel/drivers/media/pci/mantis/mantis_pcmcia.c index 2f188c089..b2dbc7b2e 100644 --- a/kernel/drivers/media/pci/mantis/mantis_pcmcia.c +++ b/kernel/drivers/media/pci/mantis/mantis_pcmcia.c @@ -89,7 +89,7 @@ int mantis_pcmcia_init(struct mantis_ca *ca) u32 gpif_stat, card_stat; - mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK); + mantis_unmask_ints(mantis, MANTIS_INT_IRQ0); gpif_stat = mmread(MANTIS_GPIF_STATUS); card_stat = mmread(MANTIS_GPIF_IRQCFG); @@ -117,5 +117,5 @@ void mantis_pcmcia_exit(struct mantis_ca *ca) struct mantis_pci *mantis = ca->ca_priv; mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS); - mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK); + mantis_mask_ints(mantis, MANTIS_INT_IRQ0); } diff --git a/kernel/drivers/media/pci/mantis/mantis_uart.c b/kernel/drivers/media/pci/mantis/mantis_uart.c index a70719218..f1c96aec8 100644 --- a/kernel/drivers/media/pci/mantis/mantis_uart.c +++ b/kernel/drivers/media/pci/mantis/mantis_uart.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "dmxdev.h" #include "dvbdev.h" @@ -35,6 +36,7 @@ #include "mantis_common.h" #include "mantis_reg.h" #include "mantis_uart.h" +#include "mantis_input.h" struct mantis_uart_params { enum mantis_baud baud_rate; @@ -59,51 +61,54 @@ static struct { { "EVEN" } }; -#define UART_MAX_BUF 16 - -static int mantis_uart_read(struct mantis_pci *mantis, u8 *data) +static void mantis_uart_read(struct mantis_pci *mantis) { struct mantis_hwconfig *config = mantis->hwconfig; - u32 stat = 0, i; + int i, scancode = 0, err = 0; /* get data */ + dprintk(MANTIS_DEBUG, 1, "UART Reading ..."); for (i = 0; i < (config->bytes + 1); i++) { + int data = mmread(MANTIS_UART_RXD); - stat = mmread(MANTIS_UART_STAT); - - if (stat & MANTIS_UART_RXFIFO_FULL) { - dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); - } - data[i] = mmread(MANTIS_UART_RXD) & 0x3f; + dprintk(MANTIS_DEBUG, 0, " <%02x>", data); - dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f); + scancode = (scancode << 8) | (data & 0x3f); + err |= data; - if (data[i] & (1 << 7)) { + if (data & (1 << 7)) dprintk(MANTIS_ERROR, 1, "UART framing error"); - return -EINVAL; - } - if (data[i] & (1 << 6)) { + + if (data & (1 << 6)) dprintk(MANTIS_ERROR, 1, "UART parity error"); - return -EINVAL; - } } + dprintk(MANTIS_DEBUG, 0, "\n"); - return 0; + if ((err & 0xC0) == 0) + mantis_input_process(mantis, scancode); } static void mantis_uart_work(struct work_struct *work) { struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); - struct mantis_hwconfig *config = mantis->hwconfig; - u8 buf[16]; - int i; + u32 stat; - mantis_uart_read(mantis, buf); + stat = mmread(MANTIS_UART_STAT); - for (i = 0; i < (config->bytes + 1); i++) - dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]); + if (stat & MANTIS_UART_RXFIFO_FULL) + dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); - dprintk(MANTIS_DEBUG, 0, "\n"); + /* + * MANTIS_UART_RXFIFO_DATA is only set if at least + * config->bytes + 1 bytes are in the FIFO. + */ + while (stat & MANTIS_UART_RXFIFO_DATA) { + mantis_uart_read(mantis); + stat = mmread(MANTIS_UART_STAT); + } + + /* re-enable UART (RX) interrupt */ + mantis_unmask_ints(mantis, MANTIS_INT_IRQ1); } static int mantis_uart_setup(struct mantis_pci *mantis, @@ -152,9 +157,6 @@ int mantis_uart_init(struct mantis_pci *mantis) rates[params.baud_rate].string, parity[params.parity].string); - init_waitqueue_head(&mantis->uart_wq); - spin_lock_init(&mantis->uart_lock); - INIT_WORK(&mantis->uart_work, mantis_uart_work); /* disable interrupt */ @@ -169,8 +171,8 @@ int mantis_uart_init(struct mantis_pci *mantis) mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL); /* enable interrupt */ - mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK); mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL); + mantis_unmask_ints(mantis, MANTIS_INT_IRQ1); schedule_work(&mantis->uart_work); dprintk(MANTIS_DEBUG, 1, "UART successfully initialized"); @@ -182,6 +184,7 @@ EXPORT_SYMBOL_GPL(mantis_uart_init); void mantis_uart_exit(struct mantis_pci *mantis) { /* disable interrupt */ + mantis_mask_ints(mantis, MANTIS_INT_IRQ1); mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); flush_work(&mantis->uart_work); } diff --git a/kernel/drivers/media/pci/mantis/mantis_vp1034.c b/kernel/drivers/media/pci/mantis/mantis_vp1034.c index 7c1bd1672..3b1928594 100644 --- a/kernel/drivers/media/pci/mantis/mantis_vp1034.c +++ b/kernel/drivers/media/pci/mantis/mantis_vp1034.c @@ -44,7 +44,7 @@ static struct mb86a16_config vp1034_mb86a16_config = { #define MANTIS_MODEL_NAME "VP-1034" #define MANTIS_DEV_TYPE "DVB-S/DSS" -int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +int vp1034_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { struct mantis_pci *mantis = fe->dvb->priv; diff --git a/kernel/drivers/media/pci/mantis/mantis_vp1034.h b/kernel/drivers/media/pci/mantis/mantis_vp1034.h index 323f38ef8..764b1c66e 100644 --- a/kernel/drivers/media/pci/mantis/mantis_vp1034.h +++ b/kernel/drivers/media/pci/mantis/mantis_vp1034.h @@ -28,6 +28,7 @@ #define MANTIS_VP_1034_DVB_S 0x0014 extern struct mantis_hwconfig vp1034_config; -extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); +extern int vp1034_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); #endif /* __MANTIS_VP1034_H */ diff --git a/kernel/drivers/media/pci/netup_unidvb/Kconfig b/kernel/drivers/media/pci/netup_unidvb/Kconfig new file mode 100644 index 000000000..f277b0b10 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/Kconfig @@ -0,0 +1,12 @@ +config DVB_NETUP_UNIDVB + tristate "NetUP Universal DVB card support" + depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER + select VIDEOBUF2_DVB + select VIDEOBUF2_VMALLOC + select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT + select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT + ---help--- + Support for NetUP PCI express Universal DVB card. + diff --git a/kernel/drivers/media/pci/netup_unidvb/Makefile b/kernel/drivers/media/pci/netup_unidvb/Makefile new file mode 100644 index 000000000..ee6ae0501 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/Makefile @@ -0,0 +1,9 @@ +netup-unidvb-objs += netup_unidvb_core.o +netup-unidvb-objs += netup_unidvb_i2c.o +netup-unidvb-objs += netup_unidvb_ci.o +netup-unidvb-objs += netup_unidvb_spi.o + +obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/kernel/drivers/media/pci/netup_unidvb/netup_unidvb.h b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb.h new file mode 100644 index 000000000..a67b28111 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb.h @@ -0,0 +1,133 @@ +/* + * netup_unidvb.h + * + * Data type definitions for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include + +#define NETUP_UNIDVB_NAME "netup_unidvb" +#define NETUP_UNIDVB_VERSION "0.0.1" +#define NETUP_VENDOR_ID 0x1b55 +#define NETUP_PCI_DEV_REVISION 0x2 + +/* IRQ-related regisers */ +#define REG_ISR 0x4890 +#define REG_ISR_MASKED 0x4892 +#define REG_IMASK_SET 0x4894 +#define REG_IMASK_CLEAR 0x4896 +/* REG_ISR register bits */ +#define NETUP_UNIDVB_IRQ_SPI (1 << 0) +#define NETUP_UNIDVB_IRQ_I2C0 (1 << 1) +#define NETUP_UNIDVB_IRQ_I2C1 (1 << 2) +#define NETUP_UNIDVB_IRQ_FRA0 (1 << 4) +#define NETUP_UNIDVB_IRQ_FRA1 (1 << 5) +#define NETUP_UNIDVB_IRQ_FRB0 (1 << 6) +#define NETUP_UNIDVB_IRQ_FRB1 (1 << 7) +#define NETUP_UNIDVB_IRQ_DMA1 (1 << 8) +#define NETUP_UNIDVB_IRQ_DMA2 (1 << 9) +#define NETUP_UNIDVB_IRQ_CI (1 << 10) +#define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) +#define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) + +struct netup_dma { + u8 num; + spinlock_t lock; + struct netup_unidvb_dev *ndev; + struct netup_dma_regs __iomem *regs; + u32 ring_buffer_size; + u8 *addr_virt; + dma_addr_t addr_phys; + u64 addr_last; + u32 high_addr; + u32 data_offset; + u32 data_size; + struct list_head free_buffers; + struct work_struct work; + struct timer_list timeout; +}; + +enum netup_i2c_state { + STATE_DONE, + STATE_WAIT, + STATE_WANT_READ, + STATE_WANT_WRITE, + STATE_ERROR +}; + +struct netup_i2c_regs; + +struct netup_i2c { + spinlock_t lock; + wait_queue_head_t wq; + struct i2c_adapter adap; + struct netup_unidvb_dev *dev; + struct netup_i2c_regs __iomem *regs; + struct i2c_msg *msg; + enum netup_i2c_state state; + u32 xmit_size; +}; + +struct netup_ci_state { + struct dvb_ca_en50221 ca; + u8 __iomem *membase8_config; + u8 __iomem *membase8_io; + struct netup_unidvb_dev *dev; + int status; + int nr; +}; + +struct netup_spi; + +struct netup_unidvb_dev { + struct pci_dev *pci_dev; + int pci_bus; + int pci_slot; + int pci_func; + int board_num; + int old_fw; + u32 __iomem *lmmio0; + u8 __iomem *bmmio0; + u32 __iomem *lmmio1; + u8 __iomem *bmmio1; + u8 *dma_virt; + dma_addr_t dma_phys; + u32 dma_size; + struct vb2_dvb_frontends frontends[2]; + struct netup_i2c i2c[2]; + struct workqueue_struct *wq; + struct netup_dma dma[2]; + struct netup_ci_state ci[2]; + struct netup_spi *spi; +}; + +int netup_i2c_register(struct netup_unidvb_dev *ndev); +void netup_i2c_unregister(struct netup_unidvb_dev *ndev); +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev); +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c); +irqreturn_t netup_spi_interrupt(struct netup_spi *spi); +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev, + int num, struct pci_dev *pci_dev); +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num); +int netup_spi_init(struct netup_unidvb_dev *ndev); +void netup_spi_release(struct netup_unidvb_dev *ndev); diff --git a/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c new file mode 100644 index 000000000..f46ffac66 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c @@ -0,0 +1,248 @@ +/* + * netup_unidvb_ci.c + * + * DVB CAM support for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "netup_unidvb.h" + +/* CI slot 0 base address */ +#define CAM0_CONFIG 0x0 +#define CAM0_IO 0x8000 +#define CAM0_MEM 0x10000 +#define CAM0_SZ 32 +/* CI slot 1 base address */ +#define CAM1_CONFIG 0x20000 +#define CAM1_IO 0x28000 +#define CAM1_MEM 0x30000 +#define CAM1_SZ 32 +/* ctrlstat registers */ +#define CAM_CTRLSTAT_READ_SET 0x4980 +#define CAM_CTRLSTAT_CLR 0x4982 +/* register bits */ +#define BIT_CAM_STCHG (1<<0) +#define BIT_CAM_PRESENT (1<<1) +#define BIT_CAM_RESET (1<<2) +#define BIT_CAM_BYPASS (1<<3) +#define BIT_CAM_READY (1<<4) +#define BIT_CAM_ERROR (1<<5) +#define BIT_CAM_OVERCURR (1<<6) +/* BIT_CAM_BYPASS bit shift for SLOT 1 */ +#define CAM1_SHIFT 8 + +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev) +{ + writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR); + return IRQ_HANDLED; +} + +static int netup_unidvb_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + if (slot != 0) + return -EINVAL; + /* pass data to CAM module */ + writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR); + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x done\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + return 0; +} + +static int netup_unidvb_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__); + return 0; +} + +static int netup_unidvb_ci_slot_reset(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + unsigned long timeout = 0; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + u16 ci_stat = 0; + int reset_counter = 3; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); +reset: + timeout = jiffies + msecs_to_jiffies(5000); + /* start reset */ + writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + dev_dbg(&dev->pci_dev->dev, "%s(): waiting for reset\n", __func__); + /* wait until reset done */ + while (time_before(jiffies, timeout)) { + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + if (ci_stat & (BIT_CAM_READY << shift)) + break; + udelay(1000); + } + if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) { + dev_dbg(&dev->pci_dev->dev, + "%s(): CAMP reset timeout! Will try again..\n", + __func__); + reset_counter--; + goto reset; + } + return 0; +} + +static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + u16 ci_stat = 0; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + if (ci_stat & (BIT_CAM_READY << shift)) { + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } else if (ci_stat & (BIT_CAM_PRESENT << shift)) { + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT; + } else { + state->status = 0; + } + return state->status; +} + +static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u8 val = *((u8 __force *)state->membase8_io + addr); + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); + return val; +} + +static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); + *((u8 __force *)state->membase8_io + addr) = data; + return 0; +} + +static int netup_unidvb_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u8 val = *((u8 __force *)state->membase8_io + addr); + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); + return val; +} + +static int netup_unidvb_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); + *((u8 __force *)state->membase8_io + addr) = data; + return 0; +} + +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev, + int num, struct pci_dev *pci_dev) +{ + int result; + struct netup_ci_state *state; + + if (num < 0 || num > 1) { + dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n", + __func__, num); + return -EINVAL; + } + state = &dev->ci[num]; + state->nr = num; + state->membase8_config = dev->bmmio1 + + ((num == 0) ? CAM0_CONFIG : CAM1_CONFIG); + state->membase8_io = dev->bmmio1 + + ((num == 0) ? CAM0_IO : CAM1_IO); + state->dev = dev; + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem; + state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem; + state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl; + state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl; + state->ca.slot_reset = netup_unidvb_ci_slot_reset; + state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown; + state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl; + state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status; + state->ca.data = state; + result = dvb_ca_en50221_init(&dev->frontends[num].adapter, + &state->ca, 0, 1); + if (result < 0) { + dev_err(&pci_dev->dev, + "%s(): dvb_ca_en50221_init result %d\n", + __func__, result); + return result; + } + writew(NETUP_UNIDVB_IRQ_CI, dev->bmmio0 + REG_IMASK_SET); + dev_info(&pci_dev->dev, + "%s(): CI adapter %d init done\n", __func__, num); + return 0; +} + +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num) +{ + struct netup_ci_state *state; + + dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__); + if (num < 0 || num > 1) { + dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n", + __func__, num); + return; + } + state = &dev->ci[num]; + dvb_ca_en50221_release(&state->ca); +} + diff --git a/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_core.c new file mode 100644 index 000000000..3fdbd81b5 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -0,0 +1,1002 @@ +/* + * netup_unidvb_core.c + * + * Main module for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netup_unidvb.h" +#include "cxd2841er.h" +#include "horus3a.h" +#include "ascot2e.h" +#include "lnbh25.h" + +static int spi_enable; +module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_VERSION(NETUP_UNIDVB_VERSION); +MODULE_LICENSE("GPL"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* Avalon-MM PCI-E registers */ +#define AVL_PCIE_IENR 0x50 +#define AVL_PCIE_ISR 0x40 +#define AVL_IRQ_ENABLE 0x80 +#define AVL_IRQ_ASSERTED 0x80 +/* GPIO registers */ +#define GPIO_REG_IO 0x4880 +#define GPIO_REG_IO_TOGGLE 0x4882 +#define GPIO_REG_IO_SET 0x4884 +#define GPIO_REG_IO_CLEAR 0x4886 +/* GPIO bits */ +#define GPIO_FEA_RESET (1 << 0) +#define GPIO_FEB_RESET (1 << 1) +#define GPIO_RFA_CTL (1 << 2) +#define GPIO_RFB_CTL (1 << 3) +#define GPIO_FEA_TU_RESET (1 << 4) +#define GPIO_FEB_TU_RESET (1 << 5) +/* DMA base address */ +#define NETUP_DMA0_ADDR 0x4900 +#define NETUP_DMA1_ADDR 0x4940 +/* 8 DMA blocks * 128 packets * 188 bytes*/ +#define NETUP_DMA_BLOCKS_COUNT 8 +#define NETUP_DMA_PACKETS_COUNT 128 +/* DMA status bits */ +#define BIT_DMA_RUN 1 +#define BIT_DMA_ERROR 2 +#define BIT_DMA_IRQ 0x200 + +/** + * struct netup_dma_regs - the map of DMA module registers + * @ctrlstat_set: Control register, write to set control bits + * @ctrlstat_clear: Control register, write to clear control bits + * @start_addr_lo: DMA ring buffer start address, lower part + * @start_addr_hi: DMA ring buffer start address, higher part + * @size: DMA ring buffer size register + Bits [0-7]: DMA packet size, 188 bytes + Bits [16-23]: packets count in block, 128 packets + Bits [24-31]: blocks count, 8 blocks + * @timeout: DMA timeout in units of 8ns + For example, value of 375000000 equals to 3 sec + * @curr_addr_lo: Current ring buffer head address, lower part + * @curr_addr_hi: Current ring buffer head address, higher part + * @stat_pkt_received: Statistic register, not tested + * @stat_pkt_accepted: Statistic register, not tested + * @stat_pkt_overruns: Statistic register, not tested + * @stat_pkt_underruns: Statistic register, not tested + * @stat_fifo_overruns: Statistic register, not tested + */ +struct netup_dma_regs { + __le32 ctrlstat_set; + __le32 ctrlstat_clear; + __le32 start_addr_lo; + __le32 start_addr_hi; + __le32 size; + __le32 timeout; + __le32 curr_addr_lo; + __le32 curr_addr_hi; + __le32 stat_pkt_received; + __le32 stat_pkt_accepted; + __le32 stat_pkt_overruns; + __le32 stat_pkt_underruns; + __le32 stat_fifo_overruns; +} __packed __aligned(1); + +struct netup_unidvb_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + u32 size; +}; + +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); +static void netup_unidvb_queue_cleanup(struct netup_dma *dma); + +static struct cxd2841er_config demod_config = { + .i2c_addr = 0xc8 +}; + +static struct horus3a_config horus3a_conf = { + .i2c_address = 0xc0, + .xtal_freq_mhz = 16, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + +static struct ascot2e_config ascot2e_conf = { + .i2c_address = 0xc2, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + +static struct lnbh25_config lnbh25_conf = { + .i2c_address = 0x10, + .data2_config = LNBH25_TEN | LNBH25_EXTM +}; + +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) +{ + u8 reg, mask; + struct netup_dma *dma = priv; + struct netup_unidvb_dev *ndev; + + if (!priv) + return -EINVAL; + ndev = dma->ndev; + dev_dbg(&ndev->pci_dev->dev, "%s(): num %d is_dvb_tc %d\n", + __func__, dma->num, is_dvb_tc); + reg = readb(ndev->bmmio0 + GPIO_REG_IO); + mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; + if (!is_dvb_tc) + reg |= mask; + else + reg &= ~mask; + writeb(reg, ndev->bmmio0 + GPIO_REG_IO); + return 0; +} + +static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev) +{ + u16 gpio_reg; + + /* enable PCI-E interrupts */ + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); + /* unreset frontends bits[0:1] */ + writeb(0x00, ndev->bmmio0 + GPIO_REG_IO); + msleep(100); + gpio_reg = + GPIO_FEA_RESET | GPIO_FEB_RESET | + GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET | + GPIO_RFA_CTL | GPIO_RFB_CTL; + writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO); + dev_dbg(&ndev->pci_dev->dev, + "%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n", + __func__, readl(ndev->bmmio0 + AVL_PCIE_IENR), + (int)readb(ndev->bmmio0 + GPIO_REG_IO)); + +} + +static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable) +{ + u32 irq_mask = (dma->num == 0 ? + NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2); + + dev_dbg(&dma->ndev->pci_dev->dev, + "%s(): DMA%d enable %d\n", __func__, dma->num, enable); + if (enable) { + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set); + writew(irq_mask, dma->ndev->bmmio0 + REG_IMASK_SET); + } else { + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear); + writew(irq_mask, dma->ndev->bmmio0 + REG_IMASK_CLEAR); + } +} + +static irqreturn_t netup_dma_interrupt(struct netup_dma *dma) +{ + u64 addr_curr; + u32 size; + unsigned long flags; + struct device *dev = &dma->ndev->pci_dev->dev; + + spin_lock_irqsave(&dma->lock, flags); + addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) | + (u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr; + /* clear IRQ */ + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); + /* sanity check */ + if (addr_curr < dma->addr_phys || + addr_curr > dma->addr_phys + dma->ring_buffer_size) { + if (addr_curr != 0) { + dev_err(dev, + "%s(): addr 0x%llx not from 0x%llx:0x%llx\n", + __func__, addr_curr, (u64)dma->addr_phys, + (u64)(dma->addr_phys + dma->ring_buffer_size)); + } + goto irq_handled; + } + size = (addr_curr >= dma->addr_last) ? + (u32)(addr_curr - dma->addr_last) : + (u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr)); + if (dma->data_size != 0) { + printk_ratelimited("%s(): lost interrupt, data size %d\n", + __func__, dma->data_size); + dma->data_size += size; + } + if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) { + dma->data_size = size; + dma->data_offset = (u32)(dma->addr_last - dma->addr_phys); + } + dma->addr_last = addr_curr; + queue_work(dma->ndev->wq, &dma->work); +irq_handled: + spin_unlock_irqrestore(&dma->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t netup_unidvb_isr(int irq, void *dev_id) +{ + struct pci_dev *pci_dev = (struct pci_dev *)dev_id; + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); + u32 reg40, reg_isr; + irqreturn_t iret = IRQ_NONE; + + /* disable interrupts */ + writel(0, ndev->bmmio0 + AVL_PCIE_IENR); + /* check IRQ source */ + reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR); + if ((reg40 & AVL_IRQ_ASSERTED) != 0) { + /* IRQ is being signaled */ + reg_isr = readw(ndev->bmmio0 + REG_ISR); + if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) { + iret = netup_i2c_interrupt(&ndev->i2c[0]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) { + iret = netup_i2c_interrupt(&ndev->i2c[1]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) { + iret = netup_spi_interrupt(ndev->spi); + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) { + iret = netup_dma_interrupt(&ndev->dma[0]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) { + iret = netup_dma_interrupt(&ndev->dma[1]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_CI) { + iret = netup_ci_interrupt(ndev); + } else { + dev_err(&pci_dev->dev, + "%s(): unknown interrupt 0x%x\n", + __func__, reg_isr); + } + } + /* re-enable interrupts */ + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); + return iret; +} + +static int netup_unidvb_queue_setup(struct vb2_queue *vq, + const void *parg, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + void *alloc_ctxs[]) +{ + struct netup_dma *dma = vb2_get_drv_priv(vq); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + + *nplanes = 1; + if (vq->num_buffers + *nbuffers < VIDEO_MAX_FRAME) + *nbuffers = VIDEO_MAX_FRAME - vq->num_buffers; + sizes[0] = PAGE_ALIGN(NETUP_DMA_PACKETS_COUNT * 188); + dev_dbg(&dma->ndev->pci_dev->dev, "%s() nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static int netup_unidvb_buf_prepare(struct vb2_buffer *vb) +{ + struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct netup_unidvb_buffer *buf = container_of(vbuf, + struct netup_unidvb_buffer, vb); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s(): buf 0x%p\n", __func__, buf); + buf->size = 0; + return 0; +} + +static void netup_unidvb_buf_queue(struct vb2_buffer *vb) +{ + unsigned long flags; + struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct netup_unidvb_buffer *buf = container_of(vbuf, + struct netup_unidvb_buffer, vb); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s(): %p\n", __func__, buf); + spin_lock_irqsave(&dma->lock, flags); + list_add_tail(&buf->list, &dma->free_buffers); + spin_unlock_irqrestore(&dma->lock, flags); + mod_timer(&dma->timeout, jiffies + msecs_to_jiffies(1000)); +} + +static int netup_unidvb_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct netup_dma *dma = vb2_get_drv_priv(q); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_dma_enable(dma, 1); + return 0; +} + +static void netup_unidvb_stop_streaming(struct vb2_queue *q) +{ + struct netup_dma *dma = vb2_get_drv_priv(q); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_dma_enable(dma, 0); + netup_unidvb_queue_cleanup(dma); +} + +static struct vb2_ops dvb_qops = { + .queue_setup = netup_unidvb_queue_setup, + .buf_prepare = netup_unidvb_buf_prepare, + .buf_queue = netup_unidvb_buf_queue, + .start_streaming = netup_unidvb_start_streaming, + .stop_streaming = netup_unidvb_stop_streaming, +}; + +static int netup_unidvb_queue_init(struct netup_dma *dma, + struct vb2_queue *vb_queue) +{ + int res; + + /* Init videobuf2 queue structure */ + vb_queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vb_queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + vb_queue->drv_priv = dma; + vb_queue->buf_struct_size = sizeof(struct netup_unidvb_buffer); + vb_queue->ops = &dvb_qops; + vb_queue->mem_ops = &vb2_vmalloc_memops; + vb_queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + res = vb2_queue_init(vb_queue); + if (res != 0) { + dev_err(&dma->ndev->pci_dev->dev, + "%s(): vb2_queue_init failed (%d)\n", __func__, res); + } + return res; +} + +static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, + int num) +{ + struct vb2_dvb_frontend *fe0, *fe1, *fe2; + + if (num < 0 || num > 1) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to init DVB bus %d\n", __func__, num); + return -ENODEV; + } + mutex_init(&ndev->frontends[num].lock); + INIT_LIST_HEAD(&ndev->frontends[num].felist); + if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL || + vb2_dvb_alloc_frontend( + &ndev->frontends[num], 2) == NULL || + vb2_dvb_alloc_frontend( + &ndev->frontends[num], 3) == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to to alllocate vb2_dvb_frontend\n", + __func__); + return -ENOMEM; + } + fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1); + fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2); + fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3); + if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): frontends has not been allocated\n", __func__); + return -EINVAL; + } + netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq); + netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq); + netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq); + fe0->dvb.name = "netup_fe0"; + fe1->dvb.name = "netup_fe1"; + fe2->dvb.name = "netup_fe2"; + fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s, + &demod_config, &ndev->i2c[num].adap); + if (fe0->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-S/S2 frontend\n", + __func__); + goto frontend_detach; + } + horus3a_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, + &horus3a_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } + if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, + &lnbh25_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach SEC frontend\n", __func__); + goto frontend_detach; + } + /* DVB-T/T2 frontend */ + fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t, + &demod_config, &ndev->i2c[num].adap); + if (fe1->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T frontend\n", __func__); + goto frontend_detach; + } + fe1->dvb.frontend->id = 1; + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T tuner frontend\n", + __func__); + goto frontend_detach; + } + /* DVB-C/C2 frontend */ + fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c, + &demod_config, &ndev->i2c[num].adap); + if (fe2->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-C frontend\n", __func__); + goto frontend_detach; + } + fe2->dvb.frontend->id = 2; + if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T/C tuner frontend\n", + __func__); + goto frontend_detach; + } + + if (vb2_dvb_register_bus(&ndev->frontends[num], + THIS_MODULE, NULL, + &ndev->pci_dev->dev, adapter_nr, 1)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to register DVB bus %d\n", + __func__, num); + goto frontend_detach; + } + dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num); + return 0; +frontend_detach: + vb2_dvb_dealloc_frontends(&ndev->frontends[num]); + return -EINVAL; +} + +static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num) +{ + if (num < 0 || num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to unregister DVB bus %d\n", + __func__, num); + return; + } + vb2_dvb_unregister_bus(&ndev->frontends[num]); + dev_info(&ndev->pci_dev->dev, + "%s(): DVB bus %d unregistered\n", __func__, num); +} + +static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev) +{ + int res; + + res = netup_unidvb_dvb_init(ndev, 0); + if (res) + return res; + res = netup_unidvb_dvb_init(ndev, 1); + if (res) { + netup_unidvb_dvb_fini(ndev, 0); + return res; + } + return 0; +} + +static int netup_unidvb_ring_copy(struct netup_dma *dma, + struct netup_unidvb_buffer *buf) +{ + u32 copy_bytes, ring_bytes; + u32 buff_bytes = NETUP_DMA_PACKETS_COUNT * 188 - buf->size; + u8 *p = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + struct netup_unidvb_dev *ndev = dma->ndev; + + if (p == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): buffer is NULL\n", __func__); + return -EINVAL; + } + p += buf->size; + if (dma->data_offset + dma->data_size > dma->ring_buffer_size) { + ring_bytes = dma->ring_buffer_size - dma->data_offset; + copy_bytes = (ring_bytes > buff_bytes) ? + buff_bytes : ring_bytes; + memcpy_fromio(p, (u8 __iomem *)(dma->addr_virt + dma->data_offset), copy_bytes); + p += copy_bytes; + buf->size += copy_bytes; + buff_bytes -= copy_bytes; + dma->data_size -= copy_bytes; + dma->data_offset += copy_bytes; + if (dma->data_offset == dma->ring_buffer_size) + dma->data_offset = 0; + } + if (buff_bytes > 0) { + ring_bytes = dma->data_size; + copy_bytes = (ring_bytes > buff_bytes) ? + buff_bytes : ring_bytes; + memcpy_fromio(p, (u8 __iomem *)(dma->addr_virt + dma->data_offset), copy_bytes); + buf->size += copy_bytes; + dma->data_size -= copy_bytes; + dma->data_offset += copy_bytes; + if (dma->data_offset == dma->ring_buffer_size) + dma->data_offset = 0; + } + return 0; +} + +static void netup_unidvb_dma_worker(struct work_struct *work) +{ + struct netup_dma *dma = container_of(work, struct netup_dma, work); + struct netup_unidvb_dev *ndev = dma->ndev; + struct netup_unidvb_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + if (dma->data_size == 0) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): data_size == 0\n", __func__); + goto work_done; + } + while (dma->data_size > 0) { + if (list_empty(&dma->free_buffers)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): no free buffers\n", __func__); + goto work_done; + } + buf = list_first_entry(&dma->free_buffers, + struct netup_unidvb_buffer, list); + if (buf->size >= NETUP_DMA_PACKETS_COUNT * 188) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): buffer overflow, size %d\n", + __func__, buf->size); + goto work_done; + } + if (netup_unidvb_ring_copy(dma, buf)) + goto work_done; + if (buf->size == NETUP_DMA_PACKETS_COUNT * 188) { + list_del(&buf->list); + dev_dbg(&ndev->pci_dev->dev, + "%s(): buffer %p done, size %d\n", + __func__, buf, buf->size); + v4l2_get_timestamp(&buf->vb.timestamp); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + } +work_done: + dma->data_size = 0; + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void netup_unidvb_queue_cleanup(struct netup_dma *dma) +{ + struct netup_unidvb_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + while (!list_empty(&dma->free_buffers)) { + buf = list_first_entry(&dma->free_buffers, + struct netup_unidvb_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void netup_unidvb_dma_timeout(unsigned long data) +{ + struct netup_dma *dma = (struct netup_dma *)data; + struct netup_unidvb_dev *ndev = dma->ndev; + + dev_dbg(&ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_queue_cleanup(dma); +} + +static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num) +{ + struct netup_dma *dma; + struct device *dev = &ndev->pci_dev->dev; + + if (num < 0 || num > 1) { + dev_err(dev, "%s(): unable to register DMA%d\n", + __func__, num); + return -ENODEV; + } + dma = &ndev->dma[num]; + dev_info(dev, "%s(): starting DMA%d\n", __func__, num); + dma->num = num; + dma->ndev = ndev; + spin_lock_init(&dma->lock); + INIT_WORK(&dma->work, netup_unidvb_dma_worker); + INIT_LIST_HEAD(&dma->free_buffers); + dma->timeout.function = netup_unidvb_dma_timeout; + dma->timeout.data = (unsigned long)dma; + init_timer(&dma->timeout); + dma->ring_buffer_size = ndev->dma_size / 2; + dma->addr_virt = ndev->dma_virt + dma->ring_buffer_size * num; + dma->addr_phys = (dma_addr_t)((u64)ndev->dma_phys + + dma->ring_buffer_size * num); + dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n", + __func__, num, dma->addr_virt, + (unsigned long long)dma->addr_phys, + dma->ring_buffer_size); + memset_io((u8 __iomem *)dma->addr_virt, 0, dma->ring_buffer_size); + dma->addr_last = dma->addr_phys; + dma->high_addr = (u32)(dma->addr_phys & 0xC0000000); + dma->regs = (struct netup_dma_regs __iomem *)(num == 0 ? + ndev->bmmio0 + NETUP_DMA0_ADDR : + ndev->bmmio0 + NETUP_DMA1_ADDR); + writel((NETUP_DMA_BLOCKS_COUNT << 24) | + (NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size); + writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo); + writel(0, &dma->regs->start_addr_hi); + writel(dma->high_addr, ndev->bmmio0 + 0x1000); + writel(375000000, &dma->regs->timeout); + msleep(1000); + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); + return 0; +} + +static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num) +{ + struct netup_dma *dma; + + if (num < 0 || num > 1) + return; + dev_dbg(&ndev->pci_dev->dev, "%s(): num %d\n", __func__, num); + dma = &ndev->dma[num]; + netup_unidvb_dma_enable(dma, 0); + msleep(50); + cancel_work_sync(&dma->work); + del_timer(&dma->timeout); +} + +static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev) +{ + int res; + + res = netup_unidvb_dma_init(ndev, 0); + if (res) + return res; + res = netup_unidvb_dma_init(ndev, 1); + if (res) { + netup_unidvb_dma_fini(ndev, 0); + return res; + } + netup_unidvb_dma_enable(&ndev->dma[0], 0); + netup_unidvb_dma_enable(&ndev->dma[1], 0); + return 0; +} + +static int netup_unidvb_ci_setup(struct netup_unidvb_dev *ndev, + struct pci_dev *pci_dev) +{ + int res; + + writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET); + res = netup_unidvb_ci_register(ndev, 0, pci_dev); + if (res) + return res; + res = netup_unidvb_ci_register(ndev, 1, pci_dev); + if (res) + netup_unidvb_ci_unregister(ndev, 0); + return res; +} + +static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) +{ + if (!request_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0), NETUP_UNIDVB_NAME)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO bar 0 at 0x%llx\n", + __func__, + (unsigned long long)pci_resource_start(pci_dev, 0)); + return -EBUSY; + } + if (!request_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1), NETUP_UNIDVB_NAME)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO bar 1 at 0x%llx\n", + __func__, + (unsigned long long)pci_resource_start(pci_dev, 1)); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + return -EBUSY; + } + return 0; +} + +static int netup_unidvb_request_modules(struct device *dev) +{ + static const char * const modules[] = { + "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL + }; + const char * const *curr_mod = modules; + int err; + + while (*curr_mod != NULL) { + err = request_module(*curr_mod); + if (err) { + dev_warn(dev, "request_module(%s) failed: %d\n", + *curr_mod, err); + } + ++curr_mod; + } + return 0; +} + +static int netup_unidvb_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + u8 board_revision; + u16 board_vendor; + struct netup_unidvb_dev *ndev; + int old_firmware = 0; + + netup_unidvb_request_modules(&pci_dev->dev); + + /* Check card revision */ + if (pci_dev->revision != NETUP_PCI_DEV_REVISION) { + dev_err(&pci_dev->dev, + "netup_unidvb: expected card revision %d, got %d\n", + NETUP_PCI_DEV_REVISION, pci_dev->revision); + dev_err(&pci_dev->dev, + "Please upgrade firmware!\n"); + dev_err(&pci_dev->dev, + "Instructions on http://www.netup.tv\n"); + old_firmware = 1; + spi_enable = 1; + } + + /* allocate device context */ + ndev = kzalloc(sizeof(*ndev), GFP_KERNEL); + + if (!ndev) + goto dev_alloc_err; + memset(ndev, 0, sizeof(*ndev)); + ndev->old_fw = old_firmware; + ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME); + if (!ndev->wq) { + dev_err(&pci_dev->dev, + "%s(): unable to create workqueue\n", __func__); + goto wq_create_err; + } + ndev->pci_dev = pci_dev; + ndev->pci_bus = pci_dev->bus->number; + ndev->pci_slot = PCI_SLOT(pci_dev->devfn); + ndev->pci_func = PCI_FUNC(pci_dev->devfn); + ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot; + pci_set_drvdata(pci_dev, ndev); + /* PCI init */ + dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n", + __func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot); + + if (pci_enable_device(pci_dev)) { + dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n", + __func__); + goto pci_enable_err; + } + /* read PCI info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision); + pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor); + if (board_vendor != NETUP_VENDOR_ID) { + dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x", + __func__, board_vendor); + goto pci_detect_err; + } + dev_info(&pci_dev->dev, + "%s(): board vendor 0x%x, revision 0x%x\n", + __func__, board_vendor, board_revision); + pci_set_master(pci_dev); + if (pci_set_dma_mask(pci_dev, 0xffffffff) < 0) { + dev_err(&pci_dev->dev, + "%s(): 32bit PCI DMA is not supported\n", __func__); + goto pci_detect_err; + } + dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__); + /* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */ + pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN | + PCI_EXP_DEVCTL_NOSNOOP_EN, 0); + /* Adjust PCIe completion timeout. */ + pcie_capability_clear_and_set_word(pci_dev, + PCI_EXP_DEVCTL2, 0xf, 0x2); + + if (netup_unidvb_request_mmio(pci_dev)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO regions\n", __func__); + goto pci_detect_err; + } + ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + if (!ndev->lmmio0) { + dev_err(&pci_dev->dev, + "%s(): unable to remap MMIO bar 0\n", __func__); + goto pci_bar0_error; + } + ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); + if (!ndev->lmmio1) { + dev_err(&pci_dev->dev, + "%s(): unable to remap MMIO bar 1\n", __func__); + goto pci_bar1_error; + } + ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0; + ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1; + dev_info(&pci_dev->dev, + "%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d", + __func__, + ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0), + ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1), + pci_dev->irq); + if (request_irq(pci_dev->irq, netup_unidvb_isr, IRQF_SHARED, + "netup_unidvb", pci_dev) < 0) { + dev_err(&pci_dev->dev, + "%s(): can't get IRQ %d\n", __func__, pci_dev->irq); + goto irq_request_err; + } + ndev->dma_size = 2 * 188 * + NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT; + ndev->dma_virt = dma_alloc_coherent(&pci_dev->dev, + ndev->dma_size, &ndev->dma_phys, GFP_KERNEL); + if (!ndev->dma_virt) { + dev_err(&pci_dev->dev, "%s(): unable to allocate DMA buffer\n", + __func__); + goto dma_alloc_err; + } + netup_unidvb_dev_enable(ndev); + if (spi_enable && netup_spi_init(ndev)) { + dev_warn(&pci_dev->dev, + "netup_unidvb: SPI flash setup failed\n"); + goto spi_setup_err; + } + if (old_firmware) { + dev_err(&pci_dev->dev, + "netup_unidvb: card initialization was incomplete\n"); + return 0; + } + if (netup_i2c_register(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n"); + goto i2c_setup_err; + } + /* enable I2C IRQs */ + writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1, + ndev->bmmio0 + REG_IMASK_SET); + usleep_range(5000, 10000); + if (netup_unidvb_dvb_setup(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n"); + goto dvb_setup_err; + } + if (netup_unidvb_ci_setup(ndev, pci_dev)) { + dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n"); + goto ci_setup_err; + } + if (netup_unidvb_dma_setup(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n"); + goto dma_setup_err; + } + dev_info(&pci_dev->dev, + "netup_unidvb: device has been initialized\n"); + return 0; +dma_setup_err: + netup_unidvb_ci_unregister(ndev, 0); + netup_unidvb_ci_unregister(ndev, 1); +ci_setup_err: + netup_unidvb_dvb_fini(ndev, 0); + netup_unidvb_dvb_fini(ndev, 1); +dvb_setup_err: + netup_i2c_unregister(ndev); +i2c_setup_err: + if (ndev->spi) + netup_spi_release(ndev); +spi_setup_err: + dma_free_coherent(&pci_dev->dev, ndev->dma_size, + ndev->dma_virt, ndev->dma_phys); +dma_alloc_err: + free_irq(pci_dev->irq, pci_dev); +irq_request_err: + iounmap(ndev->lmmio1); +pci_bar1_error: + iounmap(ndev->lmmio0); +pci_bar0_error: + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + release_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); +pci_detect_err: + pci_disable_device(pci_dev); +pci_enable_err: + pci_set_drvdata(pci_dev, NULL); + destroy_workqueue(ndev->wq); +wq_create_err: + kfree(ndev); +dev_alloc_err: + dev_err(&pci_dev->dev, + "%s(): failed to initizalize device\n", __func__); + return -EIO; +} + +static void netup_unidvb_finidev(struct pci_dev *pci_dev) +{ + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); + + dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__); + if (!ndev->old_fw) { + netup_unidvb_dma_fini(ndev, 0); + netup_unidvb_dma_fini(ndev, 1); + netup_unidvb_ci_unregister(ndev, 0); + netup_unidvb_ci_unregister(ndev, 1); + netup_unidvb_dvb_fini(ndev, 0); + netup_unidvb_dvb_fini(ndev, 1); + netup_i2c_unregister(ndev); + } + if (ndev->spi) + netup_spi_release(ndev); + writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR); + dma_free_coherent(&ndev->pci_dev->dev, ndev->dma_size, + ndev->dma_virt, ndev->dma_phys); + free_irq(pci_dev->irq, pci_dev); + iounmap(ndev->lmmio0); + iounmap(ndev->lmmio1); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + release_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, NULL); + destroy_workqueue(ndev->wq); + kfree(ndev); + dev_info(&pci_dev->dev, + "%s(): device has been successfully stopped\n", __func__); +} + + +static struct pci_device_id netup_unidvb_pci_tbl[] = { + { PCI_DEVICE(0x1b55, 0x18f6) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); + +static struct pci_driver netup_unidvb_pci_driver = { + .name = "netup_unidvb", + .id_table = netup_unidvb_pci_tbl, + .probe = netup_unidvb_initdev, + .remove = netup_unidvb_finidev, + .suspend = NULL, + .resume = NULL, +}; + +static int __init netup_unidvb_init(void) +{ + return pci_register_driver(&netup_unidvb_pci_driver); +} + +static void __exit netup_unidvb_fini(void) +{ + pci_unregister_driver(&netup_unidvb_pci_driver); +} + +module_init(netup_unidvb_init); +module_exit(netup_unidvb_fini); diff --git a/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c new file mode 100644 index 000000000..c09c52bc6 --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -0,0 +1,381 @@ +/* + * netup_unidvb_i2c.c + * + * Internal I2C bus driver for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include "netup_unidvb.h" + +#define NETUP_I2C_BUS0_ADDR 0x4800 +#define NETUP_I2C_BUS1_ADDR 0x4840 +#define NETUP_I2C_TIMEOUT 1000 + +/* twi_ctrl0_stat reg bits */ +#define TWI_IRQEN_COMPL 0x1 +#define TWI_IRQEN_ANACK 0x2 +#define TWI_IRQEN_DNACK 0x4 +#define TWI_IRQ_COMPL (TWI_IRQEN_COMPL << 8) +#define TWI_IRQ_ANACK (TWI_IRQEN_ANACK << 8) +#define TWI_IRQ_DNACK (TWI_IRQEN_DNACK << 8) +#define TWI_IRQ_TX 0x800 +#define TWI_IRQ_RX 0x1000 +#define TWI_IRQEN (TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK) +/* twi_addr_ctrl1 reg bits*/ +#define TWI_TRANSFER 0x100 +#define TWI_NOSTOP 0x200 +#define TWI_SOFT_RESET 0x2000 +/* twi_clkdiv reg value */ +#define TWI_CLKDIV 156 +/* fifo_stat_ctrl reg bits */ +#define FIFO_IRQEN 0x8000 +#define FIFO_RESET 0x4000 +/* FIFO size */ +#define FIFO_SIZE 16 + +struct netup_i2c_fifo_regs { + union { + __u8 data8; + __le16 data16; + __le32 data32; + }; + __u8 padding[4]; + __le16 stat_ctrl; +} __packed __aligned(1); + +struct netup_i2c_regs { + __le16 clkdiv; + __le16 twi_ctrl0_stat; + __le16 twi_addr_ctrl1; + __le16 length; + __u8 padding1[8]; + struct netup_i2c_fifo_regs tx_fifo; + __u8 padding2[6]; + struct netup_i2c_fifo_regs rx_fifo; +} __packed __aligned(1); + +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c) +{ + u16 reg, tmp; + unsigned long flags; + irqreturn_t iret = IRQ_HANDLED; + + spin_lock_irqsave(&i2c->lock, flags); + reg = readw(&i2c->regs->twi_ctrl0_stat); + writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat); + dev_dbg(i2c->adap.dev.parent, + "%s(): twi_ctrl0_state 0x%x\n", __func__, reg); + if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_COMPL\n", __func__); + i2c->state = STATE_DONE; + goto irq_ok; + } + if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_ANACK\n", __func__); + i2c->state = STATE_ERROR; + goto irq_ok; + } + if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_DNACK\n", __func__); + i2c->state = STATE_ERROR; + goto irq_ok; + } + if ((reg & TWI_IRQ_RX) != 0) { + tmp = readw(&i2c->regs->rx_fifo.stat_ctrl); + writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl); + i2c->state = STATE_WANT_READ; + dev_dbg(i2c->adap.dev.parent, + "%s(): want read\n", __func__); + goto irq_ok; + } + if ((reg & TWI_IRQ_TX) != 0) { + tmp = readw(&i2c->regs->tx_fifo.stat_ctrl); + writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl); + i2c->state = STATE_WANT_WRITE; + dev_dbg(i2c->adap.dev.parent, + "%s(): want write\n", __func__); + goto irq_ok; + } + dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__); + iret = IRQ_NONE; +irq_ok: + spin_unlock_irqrestore(&i2c->lock, flags); + if (iret == IRQ_HANDLED) + wake_up(&i2c->wq); + return iret; +} + +static void netup_i2c_reset(struct netup_i2c *i2c) +{ + dev_dbg(i2c->adap.dev.parent, "%s()\n", __func__); + i2c->state = STATE_DONE; + writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1); + writew(TWI_CLKDIV, &i2c->regs->clkdiv); + writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl); + writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl); + writew(0x800, &i2c->regs->tx_fifo.stat_ctrl); + writew(0x800, &i2c->regs->rx_fifo.stat_ctrl); +} + +static void netup_i2c_fifo_tx(struct netup_i2c *i2c) +{ + u8 data; + u32 fifo_space = FIFO_SIZE - + (readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f); + u32 msg_length = i2c->msg->len - i2c->xmit_size; + + msg_length = (msg_length < fifo_space ? msg_length : fifo_space); + while (msg_length--) { + data = i2c->msg->buf[i2c->xmit_size++]; + writeb(data, &i2c->regs->tx_fifo.data8); + dev_dbg(i2c->adap.dev.parent, + "%s(): write 0x%02x\n", __func__, data); + } + if (i2c->xmit_size < i2c->msg->len) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TX IRQ enabled\n", __func__); + writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN, + &i2c->regs->tx_fifo.stat_ctrl); + } +} + +static void netup_i2c_fifo_rx(struct netup_i2c *i2c) +{ + u8 data; + u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f; + + dev_dbg(i2c->adap.dev.parent, + "%s(): RX fifo size %d\n", __func__, fifo_size); + while (fifo_size--) { + data = readb(&i2c->regs->rx_fifo.data8); + if ((i2c->msg->flags & I2C_M_RD) != 0 && + i2c->xmit_size < i2c->msg->len) { + i2c->msg->buf[i2c->xmit_size++] = data; + dev_dbg(i2c->adap.dev.parent, + "%s(): read 0x%02x\n", __func__, data); + } + } + if (i2c->xmit_size < i2c->msg->len) { + dev_dbg(i2c->adap.dev.parent, + "%s(): RX IRQ enabled\n", __func__); + writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN, + &i2c->regs->rx_fifo.stat_ctrl); + } +} + +static void netup_i2c_start_xfer(struct netup_i2c *i2c) +{ + u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0); + u16 reg = readw(&i2c->regs->twi_ctrl0_stat); + + writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat); + writew(i2c->msg->len, &i2c->regs->length); + writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag, + &i2c->regs->twi_addr_ctrl1); + dev_dbg(i2c->adap.dev.parent, + "%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n", + __func__, readw(&i2c->regs->length), + readw(&i2c->regs->twi_addr_ctrl1), + readw(&i2c->regs->twi_ctrl0_stat)); + i2c->state = STATE_WAIT; + i2c->xmit_size = 0; + if (!rdflag) + netup_i2c_fifo_tx(i2c); + else + writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl), + &i2c->regs->rx_fifo.stat_ctrl); +} + +static int netup_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + unsigned long flags; + int i, trans_done, res = num; + struct netup_i2c *i2c = i2c_get_adapdata(adap); + u16 reg; + + if (num <= 0) { + dev_dbg(i2c->adap.dev.parent, + "%s(): num == %d\n", __func__, num); + return -EINVAL; + } + spin_lock_irqsave(&i2c->lock, flags); + if (i2c->state != STATE_DONE) { + dev_dbg(i2c->adap.dev.parent, + "%s(): i2c->state == %d, resetting I2C\n", + __func__, i2c->state); + netup_i2c_reset(i2c); + } + dev_dbg(i2c->adap.dev.parent, "%s() num %d\n", __func__, num); + for (i = 0; i < num; i++) { + i2c->msg = &msgs[i]; + netup_i2c_start_xfer(i2c); + trans_done = 0; + while (!trans_done) { + spin_unlock_irqrestore(&i2c->lock, flags); + if (wait_event_timeout(i2c->wq, + i2c->state != STATE_WAIT, + msecs_to_jiffies(NETUP_I2C_TIMEOUT))) { + spin_lock_irqsave(&i2c->lock, flags); + switch (i2c->state) { + case STATE_WANT_READ: + netup_i2c_fifo_rx(i2c); + break; + case STATE_WANT_WRITE: + netup_i2c_fifo_tx(i2c); + break; + case STATE_DONE: + if ((i2c->msg->flags & I2C_M_RD) != 0 && + i2c->xmit_size != i2c->msg->len) + netup_i2c_fifo_rx(i2c); + dev_dbg(i2c->adap.dev.parent, + "%s(): msg %d OK\n", + __func__, i); + trans_done = 1; + break; + case STATE_ERROR: + res = -EIO; + dev_dbg(i2c->adap.dev.parent, + "%s(): error state\n", + __func__); + goto done; + default: + dev_dbg(i2c->adap.dev.parent, + "%s(): invalid state %d\n", + __func__, i2c->state); + res = -EINVAL; + goto done; + } + if (!trans_done) { + i2c->state = STATE_WAIT; + reg = readw( + &i2c->regs->twi_ctrl0_stat); + writew(TWI_IRQEN | reg, + &i2c->regs->twi_ctrl0_stat); + } + spin_unlock_irqrestore(&i2c->lock, flags); + } else { + spin_lock_irqsave(&i2c->lock, flags); + dev_dbg(i2c->adap.dev.parent, + "%s(): wait timeout\n", __func__); + res = -ETIMEDOUT; + goto done; + } + spin_lock_irqsave(&i2c->lock, flags); + } + } +done: + spin_unlock_irqrestore(&i2c->lock, flags); + dev_dbg(i2c->adap.dev.parent, "%s(): result %d\n", __func__, res); + return res; +} + +static u32 netup_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm netup_i2c_algorithm = { + .master_xfer = netup_i2c_xfer, + .functionality = netup_i2c_func, +}; + +static struct i2c_adapter netup_i2c_adapter = { + .owner = THIS_MODULE, + .name = NETUP_UNIDVB_NAME, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &netup_i2c_algorithm, +}; + +static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num) +{ + int ret; + struct netup_i2c *i2c; + + if (bus_num < 0 || bus_num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): invalid bus_num %d\n", __func__, bus_num); + return -EINVAL; + } + i2c = &ndev->i2c[bus_num]; + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wq); + i2c->regs = (struct netup_i2c_regs __iomem *)(ndev->bmmio0 + + (bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR)); + netup_i2c_reset(i2c); + i2c->adap = netup_i2c_adapter; + i2c->adap.dev.parent = &ndev->pci_dev->dev; + i2c_set_adapdata(&i2c->adap, i2c); + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&ndev->pci_dev->dev, + "%s(): failed to add I2C adapter\n", __func__); + return ret; + } + dev_info(&ndev->pci_dev->dev, + "%s(): registered I2C bus %d at 0x%x\n", + __func__, + bus_num, (bus_num == 0 ? + NETUP_I2C_BUS0_ADDR : + NETUP_I2C_BUS1_ADDR)); + return 0; +} + +static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num) +{ + struct netup_i2c *i2c; + + if (bus_num < 0 || bus_num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): invalid bus number %d\n", __func__, bus_num); + return; + } + i2c = &ndev->i2c[bus_num]; + netup_i2c_reset(i2c); + /* remove adapter */ + i2c_del_adapter(&i2c->adap); + dev_info(&ndev->pci_dev->dev, + "netup_i2c_remove: unregistered I2C bus %d\n", bus_num); +} + +int netup_i2c_register(struct netup_unidvb_dev *ndev) +{ + int ret; + + ret = netup_i2c_init(ndev, 0); + if (ret) + return ret; + ret = netup_i2c_init(ndev, 1); + if (ret) { + netup_i2c_remove(ndev, 0); + return ret; + } + return 0; +} + +void netup_i2c_unregister(struct netup_unidvb_dev *ndev) +{ + netup_i2c_remove(ndev, 0); + netup_i2c_remove(ndev, 1); +} + diff --git a/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c new file mode 100644 index 000000000..f33c0de3e --- /dev/null +++ b/kernel/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c @@ -0,0 +1,248 @@ +/* + * netup_unidvb_spi.c + * + * Internal SPI driver for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 "netup_unidvb.h" +#include +#include +#include +#include + +#define NETUP_SPI_CTRL_IRQ 0x1000 +#define NETUP_SPI_CTRL_IMASK 0x2000 +#define NETUP_SPI_CTRL_START 0x8000 +#define NETUP_SPI_CTRL_LAST_CS 0x4000 + +#define NETUP_SPI_TIMEOUT 6000 + +enum netup_spi_state { + SPI_STATE_START, + SPI_STATE_DONE, +}; + +struct netup_spi_regs { + __u8 data[1024]; + __le16 control_stat; + __le16 clock_divider; +} __packed __aligned(1); + +struct netup_spi { + struct device *dev; + struct spi_master *master; + struct netup_spi_regs __iomem *regs; + u8 __iomem *mmio; + spinlock_t lock; + wait_queue_head_t waitq; + enum netup_spi_state state; +}; + +static char netup_spi_name[64] = "fpga"; + +static struct mtd_partition netup_spi_flash_partitions = { + .name = netup_spi_name, + .size = 0x1000000, /* 16MB */ + .offset = 0, + .mask_flags = MTD_CAP_ROM +}; + +static struct flash_platform_data spi_flash_data = { + .name = "netup0_m25p128", + .parts = &netup_spi_flash_partitions, + .nr_parts = 1, +}; + +static struct spi_board_info netup_spi_board = { + .modalias = "m25p128", + .max_speed_hz = 11000000, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &spi_flash_data, +}; + +irqreturn_t netup_spi_interrupt(struct netup_spi *spi) +{ + u16 reg; + unsigned long flags; + + if (!spi) + return IRQ_NONE; + + spin_lock_irqsave(&spi->lock, flags); + reg = readw(&spi->regs->control_stat); + if (!(reg & NETUP_SPI_CTRL_IRQ)) { + spin_unlock_irqrestore(&spi->lock, flags); + dev_dbg(&spi->master->dev, + "%s(): not mine interrupt\n", __func__); + return IRQ_NONE; + } + writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); + reg = readw(&spi->regs->control_stat); + writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat); + spi->state = SPI_STATE_DONE; + wake_up(&spi->waitq); + spin_unlock_irqrestore(&spi->lock, flags); + dev_dbg(&spi->master->dev, + "%s(): SPI interrupt handled\n", __func__); + return IRQ_HANDLED; +} + +static int netup_spi_transfer(struct spi_master *master, + struct spi_message *msg) +{ + struct netup_spi *spi = spi_master_get_devdata(master); + struct spi_transfer *t; + int result = 0; + u32 tr_size; + + /* reset CS */ + writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat); + writew(0, &spi->regs->control_stat); + list_for_each_entry(t, &msg->transfers, transfer_list) { + tr_size = t->len; + while (tr_size) { + u32 frag_offset = t->len - tr_size; + u32 frag_size = (tr_size > sizeof(spi->regs->data)) ? + sizeof(spi->regs->data) : tr_size; + int frag_last = 0; + + if (list_is_last(&t->transfer_list, + &msg->transfers) && + frag_offset + frag_size == t->len) { + frag_last = 1; + } + if (t->tx_buf) { + memcpy_toio(spi->regs->data, + t->tx_buf + frag_offset, + frag_size); + } else { + memset_io(spi->regs->data, + 0, frag_size); + } + spi->state = SPI_STATE_START; + writew((frag_size & 0x3ff) | + NETUP_SPI_CTRL_IMASK | + NETUP_SPI_CTRL_START | + (frag_last ? NETUP_SPI_CTRL_LAST_CS : 0), + &spi->regs->control_stat); + dev_dbg(&spi->master->dev, + "%s(): control_stat 0x%04x\n", + __func__, readw(&spi->regs->control_stat)); + wait_event_timeout(spi->waitq, + spi->state != SPI_STATE_START, + msecs_to_jiffies(NETUP_SPI_TIMEOUT)); + if (spi->state == SPI_STATE_DONE) { + if (t->rx_buf) { + memcpy_fromio(t->rx_buf + frag_offset, + spi->regs->data, frag_size); + } + } else { + if (spi->state == SPI_STATE_START) { + dev_dbg(&spi->master->dev, + "%s(): transfer timeout\n", + __func__); + } else { + dev_dbg(&spi->master->dev, + "%s(): invalid state %d\n", + __func__, spi->state); + } + result = -EIO; + goto done; + } + tr_size -= frag_size; + msg->actual_length += frag_size; + } + } +done: + msg->status = result; + spi_finalize_current_message(master); + return result; +} + +static int netup_spi_setup(struct spi_device *spi) +{ + return 0; +} + +int netup_spi_init(struct netup_unidvb_dev *ndev) +{ + struct spi_master *master; + struct netup_spi *nspi; + + master = spi_alloc_master(&ndev->pci_dev->dev, + sizeof(struct netup_spi)); + if (!master) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to alloc SPI master\n", __func__); + return -EINVAL; + } + nspi = spi_master_get_devdata(master); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bus_num = -1; + master->num_chipselect = 1; + master->transfer_one_message = netup_spi_transfer; + master->setup = netup_spi_setup; + spin_lock_init(&nspi->lock); + init_waitqueue_head(&nspi->waitq); + nspi->master = master; + nspi->regs = (struct netup_spi_regs __iomem *)(ndev->bmmio0 + 0x4000); + writew(2, &nspi->regs->clock_divider); + writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET); + ndev->spi = nspi; + if (spi_register_master(master)) { + ndev->spi = NULL; + dev_err(&ndev->pci_dev->dev, + "%s(): unable to register SPI bus\n", __func__); + return -EINVAL; + } + snprintf(netup_spi_name, + sizeof(netup_spi_name), + "fpga_%02x:%02x.%01x", + ndev->pci_bus, + ndev->pci_slot, + ndev->pci_func); + if (!spi_new_device(master, &netup_spi_board)) { + ndev->spi = NULL; + dev_err(&ndev->pci_dev->dev, + "%s(): unable to create SPI device\n", __func__); + return -EINVAL; + } + dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__); + return 0; +} + +void netup_spi_release(struct netup_unidvb_dev *ndev) +{ + u16 reg; + unsigned long flags; + struct netup_spi *spi = ndev->spi; + + if (!spi) + return; + + spin_lock_irqsave(&spi->lock, flags); + reg = readw(&spi->regs->control_stat); + writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); + reg = readw(&spi->regs->control_stat); + writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat); + spin_unlock_irqrestore(&spi->lock, flags); + spi_unregister_master(spi->master); + ndev->spi = NULL; +} + + diff --git a/kernel/drivers/media/pci/ngene/ngene-core.c b/kernel/drivers/media/pci/ngene/ngene-core.c index e29bc3af4..1b92d836a 100644 --- a/kernel/drivers/media/pci/ngene/ngene-core.c +++ b/kernel/drivers/media/pci/ngene/ngene-core.c @@ -1526,10 +1526,12 @@ static int init_channel(struct ngene_channel *chan) if (chan->fe2) { if (dvb_register_frontend(adapter, chan->fe2) < 0) goto err; - chan->fe2->tuner_priv = chan->fe->tuner_priv; - memcpy(&chan->fe2->ops.tuner_ops, - &chan->fe->ops.tuner_ops, - sizeof(struct dvb_tuner_ops)); + if (chan->fe) { + chan->fe2->tuner_priv = chan->fe->tuner_priv; + memcpy(&chan->fe2->ops.tuner_ops, + &chan->fe->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } } if (chan->has_demux) { diff --git a/kernel/drivers/media/pci/ngene/ngene.h b/kernel/drivers/media/pci/ngene/ngene.h index 51e2fbd18..fa30930d7 100644 --- a/kernel/drivers/media/pci/ngene/ngene.h +++ b/kernel/drivers/media/pci/ngene/ngene.h @@ -682,7 +682,7 @@ struct ngene_channel { int AudioDTOUpdated; u32 AudioDTOValue; - int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); + int (*set_tone)(struct dvb_frontend *, enum fe_sec_tone_mode); u8 lnbh; /* stuff from analog driver */ diff --git a/kernel/drivers/media/pci/pt1/pt1.c b/kernel/drivers/media/pci/pt1/pt1.c index acc35b42e..e7e442810 100644 --- a/kernel/drivers/media/pci/pt1/pt1.c +++ b/kernel/drivers/media/pci/pt1/pt1.c @@ -101,11 +101,11 @@ struct pt1_adapter { struct dmxdev dmxdev; struct dvb_frontend *fe; int (*orig_set_voltage)(struct dvb_frontend *fe, - fe_sec_voltage_t voltage); + enum fe_sec_voltage voltage); int (*orig_sleep)(struct dvb_frontend *fe); int (*orig_init)(struct dvb_frontend *fe); - fe_sec_voltage_t voltage; + enum fe_sec_voltage voltage; int sleep; }; @@ -575,7 +575,7 @@ pt1_update_power(struct pt1 *pt1) mutex_unlock(&pt1->lock); } -static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int pt1_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { struct pt1_adapter *adap; diff --git a/kernel/drivers/media/pci/pt1/va1j5jf8007s.c b/kernel/drivers/media/pci/pt1/va1j5jf8007s.c index 1b637b74e..d0e70dc0e 100644 --- a/kernel/drivers/media/pci/pt1/va1j5jf8007s.c +++ b/kernel/drivers/media/pci/pt1/va1j5jf8007s.c @@ -108,7 +108,7 @@ static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) } static int -va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) +va1j5jf8007s_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct va1j5jf8007s_state *state; @@ -387,7 +387,7 @@ static int va1j5jf8007s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { struct va1j5jf8007s_state *state; int ret; diff --git a/kernel/drivers/media/pci/pt1/va1j5jf8007t.c b/kernel/drivers/media/pci/pt1/va1j5jf8007t.c index 2db15159d..0268f20b8 100644 --- a/kernel/drivers/media/pci/pt1/va1j5jf8007t.c +++ b/kernel/drivers/media/pci/pt1/va1j5jf8007t.c @@ -98,7 +98,7 @@ static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) } static int -va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) +va1j5jf8007t_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct va1j5jf8007t_state *state; @@ -266,7 +266,7 @@ static int va1j5jf8007t_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, - fe_status_t *status) + enum fe_status *status) { struct va1j5jf8007t_state *state; int ret; diff --git a/kernel/drivers/media/pci/pt3/pt3.c b/kernel/drivers/media/pci/pt3/pt3.c index 7a37e8fe2..0d2e2b217 100644 --- a/kernel/drivers/media/pci/pt3/pt3.c +++ b/kernel/drivers/media/pci/pt3/pt3.c @@ -188,7 +188,7 @@ static int pt3_set_lna(struct dvb_frontend *fe) return ret; } -static int pt3_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +static int pt3_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) { struct pt3_adapter *adap; struct pt3_board *pt3; diff --git a/kernel/drivers/media/pci/saa7134/saa7134-alsa.c b/kernel/drivers/media/pci/saa7134/saa7134-alsa.c index ac3cd74e8..94f816244 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-alsa.c @@ -16,6 +16,9 @@ * */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -29,13 +32,6 @@ #include #include -#include "saa7134.h" -#include "saa7134-reg.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); - /* * Configuration macros */ @@ -57,11 +53,6 @@ module_param_array(enable, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s)."); MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg) - - - /* * Main chip structure */ @@ -149,11 +140,11 @@ static void saa7134_irq_alsa_done(struct saa7134_dev *dev, spin_lock(&dev->slock); if (UNSET == dev->dmasound.dma_blk) { - dprintk("irq: recording stopped\n"); + pr_debug("irq: recording stopped\n"); goto done; } if (0 != (status & 0x0f000000)) - dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); + pr_debug("irq: lost %ld\n", (status >> 24) & 0x0f); if (0 == (status & 0x10000000)) { /* odd */ if (0 == (dev->dmasound.dma_blk & 0x01)) @@ -164,13 +155,14 @@ static void saa7134_irq_alsa_done(struct saa7134_dev *dev, reg = SAA7134_RS_BA2(6); } if (0 == reg) { - dprintk("irq: field oops [%s]\n", + pr_debug("irq: field oops [%s]\n", (status & 0x10000000) ? "even" : "odd"); goto done; } if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { - dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count, + pr_debug("irq: overrun [full=%d/%d] - Blocks in %d\n", + dev->dmasound.read_count, dev->dmasound.bufsize, dev->dmasound.blocks); spin_unlock(&dev->slock); snd_pcm_stop_xrun(dev->dmasound.substream); @@ -180,10 +172,10 @@ static void saa7134_irq_alsa_done(struct saa7134_dev *dev, /* next block addr */ next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; saa_writel(reg,next_blk * dev->dmasound.blksize); - if (debug > 2) - dprintk("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n", - (status & 0x10000000) ? "even" : "odd ", next_blk, - next_blk * dev->dmasound.blksize, dev->dmasound.blocks, dev->dmasound.blksize, dev->dmasound.read_count); + pr_debug("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n", + (status & 0x10000000) ? "even" : "odd ", next_blk, + next_blk * dev->dmasound.blksize, dev->dmasound.blocks, + dev->dmasound.blksize, dev->dmasound.read_count); /* update status & wake waiting readers */ dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks; @@ -233,7 +225,7 @@ static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id) } if (loop == 10) { - dprintk("error! looping IRQ!"); + pr_debug("error! looping IRQ!"); } out: @@ -281,11 +273,11 @@ static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == dma->vaddr) { - dprintk("vmalloc_32(%d pages) failed\n", nr_pages); + pr_debug("vmalloc_32(%d pages) failed\n", nr_pages); return -ENOMEM; } - dprintk("vmalloc is at addr 0x%08lx, size=%d\n", + pr_debug("vmalloc is at addr 0x%08lx, size=%d\n", (unsigned long)dma->vaddr, nr_pages << PAGE_SHIFT); @@ -572,7 +564,7 @@ static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream break; } - dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", + pr_debug("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", runtime->format, runtime->channels, fmt, bswap ? 'b' : '-'); /* dma: setup channel 6 (= AUDIO) */ @@ -821,7 +813,7 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) int amux, err; if (!saa7134) { - printk(KERN_ERR "BUG: saa7134 can't find device struct." + pr_err("BUG: saa7134 can't find device struct." " Can't proceed with open\n"); return -ENODEV; } @@ -1175,7 +1167,7 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) (void*) &dev->dmasound); if (err < 0) { - printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n", + pr_err("%s: can't get IRQ %d for ALSA\n", dev->name, dev->pci->irq); goto __nodev; } @@ -1196,7 +1188,8 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, chip->iobase, chip->irq); - printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]); + pr_info("%s/alsa: %s registered as card %d\n", + dev->name, card->longname, index[devnum]); if ((err = snd_card_register(card)) == 0) { snd_saa7134_cards[devnum] = card; @@ -1218,6 +1211,8 @@ static int alsa_device_init(struct saa7134_dev *dev) static int alsa_device_exit(struct saa7134_dev *dev) { + if (!snd_saa7134_cards[dev->nr]) + return 1; snd_card_free(snd_saa7134_cards[dev->nr]); snd_saa7134_cards[dev->nr] = NULL; @@ -1240,19 +1235,19 @@ static int saa7134_alsa_init(void) saa7134_dmasound_init = alsa_device_init; saa7134_dmasound_exit = alsa_device_exit; - printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n"); + pr_info("saa7134 ALSA driver for DMA sound loaded\n"); list_for_each(list,&saa7134_devlist) { dev = list_entry(list, struct saa7134_dev, devlist); if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) - printk(KERN_INFO "%s/alsa: %s doesn't support digital audio\n", + pr_info("%s/alsa: %s doesn't support digital audio\n", dev->name, saa7134_boards[dev->board].name); else alsa_device_init(dev); } if (dev == NULL) - printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n"); + pr_info("saa7134 ALSA: no saa7134 cards found\n"); return 0; @@ -1267,12 +1262,13 @@ static void saa7134_alsa_exit(void) int idx; for (idx = 0; idx < SNDRV_CARDS; idx++) { - snd_card_free(snd_saa7134_cards[idx]); + if (snd_saa7134_cards[idx]) + snd_card_free(snd_saa7134_cards[idx]); } saa7134_dmasound_init = NULL; saa7134_dmasound_exit = NULL; - printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n"); + pr_info("saa7134 ALSA driver for DMA sound unloaded\n"); return; } diff --git a/kernel/drivers/media/pci/saa7134/saa7134-cards.c b/kernel/drivers/media/pci/saa7134/saa7134-cards.c index 3ca078057..29d2094c4 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-cards.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-cards.c @@ -20,13 +20,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include #include -#include "saa7134-reg.h" -#include "saa7134.h" #include "tuner-xc2028.h" #include #include @@ -5850,6 +5851,75 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, } }, }, + [SAA7134_BOARD_AVERMEDIA_505] = { + /* much like the "studio" version but without radio + * and another tuner (dbaryshkov@gmail.com) */ + .name = "AverMedia AverTV/505", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM] = { + .name = "Leadtek Winfast TV2100 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x0d, + .inputs = {{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE1, + .gpio = 0x00, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x08, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x08, + } }, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x04, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x08, + }, + }, }; @@ -7108,6 +7178,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1905, /* WIS */ .subdevice = 0x7007, .driver_data = SAA7134_BOARD_WIS_VOYAGER, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa10a, + .driver_data = SAA7134_BOARD_AVERMEDIA_505, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x107d, + .subdevice = 0x6f3a, + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -7158,10 +7240,10 @@ MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl); static void board_flyvideo(struct saa7134_dev *dev) { - printk("%s: there are different flyvideo cards with different tuners\n" - "%s: out there, you might have to use the tuner= insmod\n" - "%s: option to override the default value.\n", - dev->name, dev->name, dev->name); + pr_warn("%s: there are different flyvideo cards with different tuners\n" + "%s: out there, you might have to use the tuner= insmod\n" + "%s: option to override the default value.\n", + dev->name, dev->name, dev->name); } static int saa7134_xc2028_callback(struct saa7134_dev *dev, @@ -7194,7 +7276,7 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, saa7134_set_gpio(dev, 20, 1); break; } - return 0; + return 0; } return -EINVAL; } @@ -7380,7 +7462,7 @@ int saa7134_tuner_callback(void *priv, int component, int command, int arg) return saa7134_xc5000_callback(dev, command, arg); } } else { - printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); + pr_err("saa7134: Error - device struct undefined.\n"); return -EINVAL; } return -EINVAL; @@ -7411,12 +7493,12 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: - printk(KERN_WARNING "%s: warning: " + pr_warn("%s: warning: " "unknown hauppauge model #%d\n", dev->name, tv.model); break; } - printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + pr_info("%s: hauppauge eeprom: model=%d\n", dev->name, tv.model); } @@ -7427,7 +7509,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) /* Always print gpio, often manufacturers encode tuner type and other info. */ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0); dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); - printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value); + pr_info("%s: board init: gpio is %x\n", dev->name, dev->gpio_value); switch (dev->board) { case SAA7134_BOARD_FLYVIDEO2000: @@ -7448,8 +7530,9 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: - case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: + case SAA7134_BOARD_AVERMEDIA_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: @@ -7504,6 +7587,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + case SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -7512,10 +7596,10 @@ int saa7134_board_init1(struct saa7134_dev *dev) dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_MD5044: - printk("%s: seems there are two different versions of the MD5044\n" - "%s: (with the same ID) out there. If sound doesn't work for\n" - "%s: you try the audio_clock_override=0x200000 insmod option.\n", - dev->name,dev->name,dev->name); + pr_warn("%s: seems there are two different versions of the MD5044\n" + "%s: (with the same ID) out there. If sound doesn't work for\n" + "%s: you try the audio_clock_override=0x200000 insmod option.\n", + dev->name, dev->name, dev->name); break; case SAA7134_BOARD_CINERGY400_CARDBUS: /* power-up tuner chip */ @@ -7640,10 +7724,10 @@ int saa7134_board_init1(struct saa7134_dev *dev) dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: - printk("%s: %s: dual saa713x broadcast decoders\n" - "%s: Sorry, none of the inputs to this chip are supported yet.\n" - "%s: Dual decoder functionality is disabled for now, use the other chip.\n", - dev->name,card(dev).name,dev->name,dev->name); + pr_warn("%s: %s: dual saa713x broadcast decoders\n" + "%s: Sorry, none of the inputs to this chip are supported yet.\n" + "%s: Dual decoder functionality is disabled for now, use the other chip.\n", + dev->name, card(dev).name, dev->name, dev->name); break; case SAA7134_BOARD_AVERMEDIA_M102: /* enable tuner */ @@ -7789,7 +7873,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) if (board == dev->board) break; dev->board = board; - printk("%s: board type fixup: %s\n", dev->name, + pr_warn("%s: board type fixup: %s\n", dev->name, saa7134_boards[dev->board].name); dev->tuner_type = saa7134_boards[dev->board].tuner_type; @@ -7797,10 +7881,11 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_MD7134: { u8 subaddr; - u8 data[3]; + u8 data[3], data1[] = { 0x09, 0x9f, 0x86, 0x11}; int ret, tuner_t; - struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1}, - {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}}; + struct i2c_msg msg[] = {{.addr = 0x50, .flags = 0, .buf = &subaddr, .len = 1}, + {.addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = 3}}, + msg1 = {.addr = 0x61, .flags = 0, .buf = data1, .len = sizeof(data1)}; subaddr= 0x14; tuner_t = 0; @@ -7810,7 +7895,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) */ ret = i2c_transfer(&dev->i2c_adap, msg, 2); if (ret != 2) { - printk(KERN_ERR "EEPROM read failure\n"); + pr_err("EEPROM read failure\n"); } else if ((data[0] != 0) && (data[0] != 0xff)) { /* old config structure */ subaddr = data[0] + 2; @@ -7825,7 +7910,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3; break; default: - printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); + pr_err("%s Can't determine tuner type %x from EEPROM\n", + dev->name, tuner_t); } } else if ((data[1] != 0) && (data[1] != 0xff)) { /* new config structure */ @@ -7842,16 +7928,28 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case 0x001d: dev->tuner_type = TUNER_PHILIPS_FMD1216ME_MK3; - printk(KERN_INFO "%s Board has DVB-T\n", dev->name); + pr_info("%s Board has DVB-T\n", + dev->name); break; default: - printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); + pr_err("%s Can't determine tuner type %x from EEPROM\n", + dev->name, tuner_t); } } else { - printk(KERN_ERR "%s unexpected config structure\n", dev->name); + pr_err("%s unexpected config structure\n", dev->name); } - printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type); + pr_info("%s Tuner type is %d\n", dev->name, dev->tuner_type); + + /* The tuner TUNER_PHILIPS_FMD1216ME_MK3 after hardware */ + /* start has disabled IF and enabled DVB-T. When saa7134 */ + /* scan I2C devices it will not detect IF tda9887 and can`t*/ + /* watch TV without software reboot. To solve this problem */ + /* switch the tuner to analog TV mode manually. */ + if (dev->tuner_type == TUNER_PHILIPS_FMD1216ME_MK3) { + if (i2c_transfer(&dev->i2c_adap, &msg1, 1) != 1) + printk(KERN_WARNING "%s: Unable to enable IF of the tuner.\n", dev->name); + } break; } case SAA7134_BOARD_PHILIPS_EUROPA: @@ -7859,7 +7957,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* Reconfigure board as Snake reference design */ dev->board = SAA7134_BOARD_PHILIPS_SNAKE; dev->tuner_type = saa7134_boards[dev->board].tuner_type; - printk(KERN_INFO "%s: Reconfigured board as %s\n", + pr_info("%s: Reconfigured board as %s\n", dev->name, saa7134_boards[dev->board].name); break; } @@ -7887,7 +7985,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; if (dev->autodetected && (dev->eedata[0x49] == 0x50)) { dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; - printk(KERN_INFO "%s: Reconfigured board as %s\n", + pr_info("%s: Reconfigured board as %s\n", dev->name, saa7134_boards[dev->board].name); } if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { @@ -7903,13 +8001,14 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_ASUSTeK_TVFM7135: /* The card below is detected as card=53, but is different */ if (dev->autodetected && (dev->eedata[0x27] == 0x03)) { - dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; - printk(KERN_INFO "%s: P7131 analog only, using " - "entry of %s\n", - dev->name, saa7134_boards[dev->board].name); + dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; + pr_info("%s: P7131 analog only, using entry of %s\n", + dev->name, saa7134_boards[dev->board].name); - /* IR init has already happened for other cards, so - * we have to catch up. */ + /* + * IR init has already happened for other cards, so + * we have to catch up. + */ dev->has_remote = SAA7134_REMOTE_GPIO; saa7134_input_init1(dev); } @@ -7972,12 +8071,12 @@ int saa7134_board_init2(struct saa7134_dev *dev) msg.addr = 0x0b; msg.len = 1; if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) { - printk(KERN_WARNING "%s: send wake up byte to pic16C505" + pr_warn("%s: send wake up byte to pic16C505" "(IR chip) failed\n", dev->name); } else { msg.flags = I2C_M_RD; rc = i2c_transfer(&dev->i2c_adap, &msg, 1); - printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n", + pr_info("%s: probe IR chip @ i2c 0x%02x: %s\n", dev->name, msg.addr, (1 == rc) ? "yes" : "no"); if (rc == 1) @@ -8018,10 +8117,10 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; dev->tuner_type = saa7134_boards[dev->board].tuner_type; dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; - printk(KERN_INFO "%s: Reconfigured board as %s\n", + pr_info("%s: Reconfigured board as %s\n", dev->name, saa7134_boards[dev->board].name); } else { - printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n", + pr_warn("%s: Unexpected tuner type info: %x in eeprom\n", dev->name, dev->eedata[0x41]); break; } @@ -8043,9 +8142,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) msg.buf = &buffer[i][0]; msg.len = ARRAY_SIZE(buffer[0]); if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - printk(KERN_WARNING - "%s: Unable to enable tuner(%i).\n", - dev->name, i); + pr_warn("%s: Unable to enable tuner(%i).\n", + dev->name, i); } break; } @@ -8061,9 +8159,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* watch TV without software reboot. For solve this problem */ /* switch the tuner to analog TV mode manually. */ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - printk(KERN_WARNING - "%s: Unable to enable IF of the tuner.\n", - dev->name); + pr_warn("%s: Unable to enable IF of the tuner.\n", + dev->name); break; } case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: diff --git a/kernel/drivers/media/pci/saa7134/saa7134-core.c b/kernel/drivers/media/pci/saa7134/saa7134-core.c index a349e964e..f720cea80 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-core.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-core.c @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -33,9 +36,6 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -102,8 +102,15 @@ static unsigned int saa7134_devcount; int (*saa7134_dmasound_init)(struct saa7134_dev *dev); int (*saa7134_dmasound_exit)(struct saa7134_dev *dev); -#define dprintk(fmt, arg...) if (core_debug) \ - printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) +#define core_dbg(fmt, arg...) do { \ + if (core_debug) \ + printk(KERN_DEBUG pr_fmt("core: " fmt), ## arg); \ + } while (0) + +#define irq_dbg(level, fmt, arg...) do {\ + if (irq_debug > level) \ + printk(KERN_DEBUG pr_fmt("irq: " fmt), ## arg); \ + } while (0) void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) { @@ -116,8 +123,7 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN); mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff; status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff; - printk(KERN_DEBUG - "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", + core_dbg("%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", dev->name, mode, (~mode) & status, mode & status, msg); } @@ -128,7 +134,8 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) index = 1 << bit_no; switch (value) { case 0: /* static value */ - case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); + case 1: + core_dbg("setting GPIO%d to static %d\n", bit_no, value); /* turn sync mode off if necessary */ if (index & 0x00c00000) saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); @@ -140,7 +147,7 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); break; case 3: /* tristate */ - dprintk("setting GPIO%d to tristate\n", bit_no); + core_dbg("setting GPIO%d to tristate\n", bit_no); saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); break; } @@ -209,13 +216,14 @@ int saa7134_buffer_count(unsigned int size, unsigned int count) int saa7134_buffer_startpage(struct saa7134_buf *buf) { - return saa7134_buffer_pages(vb2_plane_size(&buf->vb2, 0)) * buf->vb2.v4l2_buf.index; + return saa7134_buffer_pages(vb2_plane_size(&buf->vb2.vb2_buf, 0)) + * buf->vb2.vb2_buf.index; } unsigned long saa7134_buffer_base(struct saa7134_buf *buf) { unsigned long base; - struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2.vb2_buf, 0); base = saa7134_buffer_startpage(buf) * 4096; base += dma->sgl[0].offset; @@ -274,7 +282,7 @@ int saa7134_buffer_queue(struct saa7134_dev *dev, unsigned long flags; spin_lock_irqsave(&dev->slock, flags); - dprintk("buffer_queue %p\n", buf); + core_dbg("buffer_queue %p\n", buf); if (NULL == q->curr) { if (!q->need_two) { q->curr = buf; @@ -298,12 +306,12 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, unsigned int state) { - dprintk("buffer_finish %p\n", q->curr); + core_dbg("buffer_finish %p\n", q->curr); /* finish current buffer */ - v4l2_get_timestamp(&q->curr->vb2.v4l2_buf.timestamp); - q->curr->vb2.v4l2_buf.sequence = q->seq_nr++; - vb2_buffer_done(&q->curr->vb2, state); + v4l2_get_timestamp(&q->curr->vb2.timestamp); + q->curr->vb2.sequence = q->seq_nr++; + vb2_buffer_done(&q->curr->vb2.vb2_buf, state); q->curr = NULL; } @@ -318,18 +326,18 @@ void saa7134_buffer_next(struct saa7134_dev *dev, if (!list_empty(&q->queue)) { /* activate next one from queue */ buf = list_entry(q->queue.next, struct saa7134_buf, entry); - dprintk("buffer_next %p [prev=%p/next=%p]\n", + core_dbg("buffer_next %p [prev=%p/next=%p]\n", buf, q->queue.prev, q->queue.next); list_del(&buf->entry); if (!list_empty(&q->queue)) next = list_entry(q->queue.next, struct saa7134_buf, entry); q->curr = buf; buf->activate(dev, buf, next); - dprintk("buffer_next #2 prev=%p/next=%p\n", + core_dbg("buffer_next #2 prev=%p/next=%p\n", q->queue.prev, q->queue.next); } else { /* nothing to do -- just stop DMA */ - dprintk("buffer_next %p\n", NULL); + core_dbg("buffer_next %p\n", NULL); saa7134_set_dmabits(dev); del_timer(&q->timeout); } @@ -351,7 +359,7 @@ void saa7134_buffer_timeout(unsigned long data) /* flag current buffer as failed, try to start over with the next one. */ if (q->curr) { - dprintk("timeout on %p\n", q->curr); + core_dbg("timeout on %p\n", q->curr); saa7134_buffer_finish(dev, q, VB2_BUF_STATE_ERROR); } saa7134_buffer_next(dev, q); @@ -368,7 +376,8 @@ void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q) if (!list_empty(&q->queue)) { list_for_each_safe(pos, n, &q->queue) { tmp = list_entry(pos, struct saa7134_buf, entry); - vb2_buffer_done(&tmp->vb2, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&tmp->vb2.vb2_buf, + VB2_BUF_STATE_ERROR); list_del(pos); tmp = NULL; } @@ -474,7 +483,7 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) SAA7134_MAIN_CTRL_TE5 | SAA7134_MAIN_CTRL_TE6, ctrl); - dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n", + core_dbg("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n", task, ctrl, irq, split ? "no" : "yes"); return 0; @@ -496,21 +505,21 @@ static void print_irqstatus(struct saa7134_dev *dev, int loop, { unsigned int i; - printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx", - dev->name,loop,jiffies,report,status); + irq_dbg(1, "[%d,%ld]: r=0x%lx s=0x%02lx", + loop, jiffies, report, status); for (i = 0; i < IRQBITS; i++) { if (!(report & (1 << i))) continue; - printk(" %s",irqbits[i]); + pr_cont(" %s", irqbits[i]); } if (report & SAA7134_IRQ_REPORT_DONE_RA0) { - printk(" | RA0=%s,%s,%s,%ld", - (status & 0x40) ? "vbi" : "video", - (status & 0x20) ? "b" : "a", - (status & 0x10) ? "odd" : "even", - (status & 0x0f)); + pr_cont(" | RA0=%s,%s,%s,%ld", + (status & 0x40) ? "vbi" : "video", + (status & 0x20) ? "b" : "a", + (status & 0x10) ? "odd" : "even", + (status & 0x0f)); } - printk("\n"); + pr_cont("\n"); } static irqreturn_t saa7134_irq(int irq, void *dev_id) @@ -532,16 +541,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && (dev->dmasound.priv_data != NULL) ) { - if (irq_debug > 1) - printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n", - dev->name); + irq_dbg(2, "preserving DMA sound interrupt\n"); report &= ~SAA7134_IRQ_REPORT_DONE_RA3; } if (0 == report) { - if (irq_debug > 1) - printk(KERN_DEBUG "%s/irq: no (more) work\n", - dev->name); + irq_dbg(2, "no (more) work\n"); goto out; } @@ -614,24 +619,24 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) print_irqstatus(dev,loop,report,status); if (report & SAA7134_IRQ_REPORT_PE) { /* disable all parity error */ - printk(KERN_WARNING "%s/irq: looping -- " + pr_warn("%s/irq: looping -- " "clearing PE (parity error!) enable bit\n",dev->name); saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE); } else if (report & SAA7134_IRQ_REPORT_GPIO16) { /* disable gpio16 IRQ */ - printk(KERN_WARNING "%s/irq: looping -- " + pr_warn("%s/irq: looping -- " "clearing GPIO16 enable bit\n",dev->name); saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P); saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N); } else if (report & SAA7134_IRQ_REPORT_GPIO18) { /* disable gpio18 IRQs */ - printk(KERN_WARNING "%s/irq: looping -- " + pr_warn("%s/irq: looping -- " "clearing GPIO18 enable bit\n",dev->name); saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N); } else { /* disable all irqs */ - printk(KERN_WARNING "%s/irq: looping -- " + pr_warn("%s/irq: looping -- " "clearing all enable bits\n",dev->name); saa_writel(SAA7134_IRQ1,0); saa_writel(SAA7134_IRQ2,0); @@ -680,7 +685,7 @@ static int saa7134_hw_enable1(struct saa7134_dev *dev) static int saa7134_hwinit1(struct saa7134_dev *dev) { - dprintk("hwinit1\n"); + core_dbg("hwinit1\n"); saa_writel(SAA7134_IRQ1, 0); saa_writel(SAA7134_IRQ2, 0); @@ -742,7 +747,7 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev) static int saa7134_hwinit2(struct saa7134_dev *dev) { - dprintk("hwinit2\n"); + core_dbg("hwinit2\n"); saa7134_video_init2(dev); saa7134_tvaudio_init2(dev); @@ -756,7 +761,7 @@ static int saa7134_hwinit2(struct saa7134_dev *dev) /* shutdown */ static int saa7134_hwfini(struct saa7134_dev *dev) { - dprintk("hwfini\n"); + core_dbg("hwfini\n"); if (card_has_mpeg(dev)) saa7134_ts_fini(dev); @@ -772,34 +777,32 @@ static void must_configure_manually(int has_eeprom) unsigned int i,p; if (!has_eeprom) - printk(KERN_WARNING - "saa7134: \n" - "saa7134: Congratulations! Your TV card vendor saved a few\n" - "saa7134: cents for a eeprom, thus your pci board has no\n" - "saa7134: subsystem ID and I can't identify it automatically\n" - "saa7134: \n" - "saa7134: I feel better now. Ok, here are the good news:\n" - "saa7134: You can use the card= insmod option to specify\n" - "saa7134: which board do you have. The list:\n"); + pr_warn("saa7134: \n" + "saa7134: Congratulations! Your TV card vendor saved a few\n" + "saa7134: cents for a eeprom, thus your pci board has no\n" + "saa7134: subsystem ID and I can't identify it automatically\n" + "saa7134: \n" + "saa7134: I feel better now. Ok, here are the good news:\n" + "saa7134: You can use the card= insmod option to specify\n" + "saa7134: which board do you have. The list:\n"); else - printk(KERN_WARNING - "saa7134: Board is currently unknown. You might try to use the card=\n" - "saa7134: insmod option to specify which board do you have, but this is\n" - "saa7134: somewhat risky, as might damage your card. It is better to ask\n" - "saa7134: for support at linux-media@vger.kernel.org.\n" - "saa7134: The supported cards are:\n"); + pr_warn("saa7134: Board is currently unknown. You might try to use the card=\n" + "saa7134: insmod option to specify which board do you have, but this is\n" + "saa7134: somewhat risky, as might damage your card. It is better to ask\n" + "saa7134: for support at linux-media@vger.kernel.org.\n" + "saa7134: The supported cards are:\n"); for (i = 0; i < saa7134_bcount; i++) { - printk(KERN_WARNING "saa7134: card=%d -> %-40.40s", + pr_warn("saa7134: card=%d -> %-40.40s", i,saa7134_boards[i].name); for (p = 0; saa7134_pci_tbl[p].driver_data; p++) { if (saa7134_pci_tbl[p].driver_data != i) continue; - printk(" %04x:%04x", + pr_cont(" %04x:%04x", saa7134_pci_tbl[p].subvendor, saa7134_pci_tbl[p].subdevice); } - printk("\n"); + pr_cont("\n"); } } @@ -903,31 +906,31 @@ static int saa7134_initdev(struct pci_dev *pci_dev, /* pci quirks */ if (pci_pci_problems) { if (pci_pci_problems & PCIPCI_TRITON) - printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name); + pr_info("%s: quirk: PCIPCI_TRITON\n", dev->name); if (pci_pci_problems & PCIPCI_NATOMA) - printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name); + pr_info("%s: quirk: PCIPCI_NATOMA\n", dev->name); if (pci_pci_problems & PCIPCI_VIAETBF) - printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name); + pr_info("%s: quirk: PCIPCI_VIAETBF\n", dev->name); if (pci_pci_problems & PCIPCI_VSFX) - printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name); + pr_info("%s: quirk: PCIPCI_VSFX\n", dev->name); #ifdef PCIPCI_ALIMAGIK if (pci_pci_problems & PCIPCI_ALIMAGIK) { - printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", + pr_info("%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", dev->name); latency = 0x0A; } #endif if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) { - printk(KERN_INFO "%s: quirk: this driver and your " + pr_info("%s: quirk: this driver and your " "chipset may not work together" " in overlay mode.\n",dev->name); if (!saa7134_no_overlay) { - printk(KERN_INFO "%s: quirk: overlay " + pr_info("%s: quirk: overlay " "mode will be disabled.\n", dev->name); saa7134_no_overlay = 1; } else { - printk(KERN_INFO "%s: quirk: overlay " + pr_info("%s: quirk: overlay " "mode will be forced. Use this" " option at your own risk.\n", dev->name); @@ -935,7 +938,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, } } if (UNSET != latency) { - printk(KERN_INFO "%s: setting pci latency timer to %d\n", + pr_info("%s: setting pci latency timer to %d\n", dev->name,latency); pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); } @@ -943,14 +946,14 @@ static int saa7134_initdev(struct pci_dev *pci_dev, /* print pci info */ dev->pci_rev = pci_dev->revision; pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); - printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " + pr_info("%s: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { - printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); - err = -EIO; + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + pr_warn("%s: Oops: no 32bit PCI DMA ???\n", dev->name); goto fail1; } @@ -972,7 +975,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[dev->nr]) dev->tuner_type = tuner[dev->nr]; - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", dev->name,pci_dev->subsystem_vendor, pci_dev->subsystem_device,saa7134_boards[dev->board].name, dev->board, dev->autodetected ? @@ -983,7 +986,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, pci_resource_len(pci_dev,0), dev->name)) { err = -EBUSY; - printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n", + pr_err("%s: can't get MMIO memory @ 0x%llx\n", dev->name,(unsigned long long)pci_resource_start(pci_dev,0)); goto fail1; } @@ -992,7 +995,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->bmmio = (__u8 __iomem *)dev->lmmio; if (NULL == dev->lmmio) { err = -EIO; - printk(KERN_ERR "%s: can't ioremap() MMIO memory\n", + pr_err("%s: can't ioremap() MMIO memory\n", dev->name); goto fail2; } @@ -1010,7 +1013,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, err = request_irq(pci_dev->irq, saa7134_irq, IRQF_SHARED, dev->name, dev); if (err < 0) { - printk(KERN_ERR "%s: can't get IRQ %d\n", + pr_err("%s: can't get IRQ %d\n", dev->name,pci_dev->irq); goto fail4; } @@ -1040,7 +1043,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, &dev->i2c_adap, "saa6588", 0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr)); if (sd) { - printk(KERN_INFO "%s: found RDS decoder\n", dev->name); + pr_info("%s: found RDS decoder\n", dev->name); dev->has_rds = 1; } } @@ -1059,7 +1062,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, /* register v4l devices */ if (saa7134_no_overlay > 0) - printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); + pr_info("%s: Overlay support disabled.\n", dev->name); dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); dev->video_dev->ctrl_handler = &dev->ctrl_handler; @@ -1068,11 +1071,11 @@ static int saa7134_initdev(struct pci_dev *pci_dev, err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[dev->nr]); if (err < 0) { - printk(KERN_INFO "%s: can't register video device\n", + pr_info("%s: can't register video device\n", dev->name); goto fail5; } - printk(KERN_INFO "%s: registered device %s [v4l2]\n", + pr_info("%s: registered device %s [v4l2]\n", dev->name, video_device_node_name(dev->video_dev)); dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); @@ -1084,7 +1087,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, vbi_nr[dev->nr]); if (err < 0) goto fail5; - printk(KERN_INFO "%s: registered device %s\n", + pr_info("%s: registered device %s\n", dev->name, video_device_node_name(dev->vbi_dev)); if (card_has_radio(dev)) { @@ -1095,7 +1098,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, radio_nr[dev->nr]); if (err < 0) goto fail5; - printk(KERN_INFO "%s: registered device %s\n", + pr_info("%s: registered device %s\n", dev->name, video_device_node_name(dev->radio_dev)); } @@ -1204,12 +1207,12 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev, buf = q->curr; next = buf; - dprintk("buffer_requeue\n"); + core_dbg("buffer_requeue\n"); if (!buf) return 0; - dprintk("buffer_requeue : resending active buffers \n"); + core_dbg("buffer_requeue : resending active buffer\n"); if (!list_empty(&q->queue)) next = list_entry(q->queue.next, struct saa7134_buf, @@ -1358,7 +1361,7 @@ static struct pci_driver saa7134_pci_driver = { static int __init saa7134_init(void) { INIT_LIST_HEAD(&saa7134_devlist); - printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n", + pr_info("saa7130/34: v4l2 driver version %s loaded\n", SAA7134_VERSION); return pci_register_driver(&saa7134_pci_driver); } diff --git a/kernel/drivers/media/pci/saa7134/saa7134-dvb.c b/kernel/drivers/media/pci/saa7134/saa7134-dvb.c index 73ffbabf8..101ba8729 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-dvb.c @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -28,8 +31,6 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" #include #include "dvb-pll.h" #include @@ -75,19 +76,8 @@ static int use_frontend; module_param(use_frontend, int, 0644); MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); - DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define dprintk(fmt, arg...) do { if (debug) \ - printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) - -/* Print a warning */ -#define wprintk(fmt, arg...) \ - printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg) - /* ------------------------------------------------------------------ * mt352 based DVB-T cards */ @@ -112,7 +102,7 @@ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); udelay(10); ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); - dprintk("%s %s\n", __func__, ok ? "on" : "off"); + pr_debug("%s %s\n", __func__, ok ? "on" : "off"); if (!ok) saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); @@ -130,9 +120,8 @@ static int mt352_pinnacle_init(struct dvb_frontend* fe) static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f }; static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d }; static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; - struct saa7134_dev *dev= fe->dvb->priv; - dprintk("%s called\n", __func__); + pr_debug("%s called\n", __func__); mt352_write(fe, clock_config, sizeof(clock_config)); udelay(200); @@ -258,7 +247,7 @@ static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable) struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2}; if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { - wprintk("could not access the I2C gate\n"); + pr_warn("could not access the I2C gate\n"); return -EIO; } if (enable) @@ -266,7 +255,7 @@ static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable) else msg.buf = msg_disable; if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { - wprintk("could not access the I2C gate\n"); + pr_warn("could not access the I2C gate\n"); return -EIO; } msleep(20); @@ -369,7 +358,7 @@ static int philips_tda6651_pll_set(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { - wprintk("could not write to tuner at addr: 0x%02x\n", + pr_warn("could not write to tuner at addr: 0x%02x\n", addr << 1); return -EIO; } @@ -556,8 +545,7 @@ static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) tda8290_msg.buf = tda8290_open; } if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) { - struct saa7134_dev *dev = fe->dvb->priv; - wprintk("could not access tda8290 I2C gate\n"); + pr_warn("could not access tda8290 I2C gate\n"); return -EIO; } msleep(20); @@ -570,11 +558,14 @@ static int philips_tda827x_tuner_init(struct dvb_frontend *fe) struct tda1004x_state *state = fe->demodulator_priv; switch (state->config->antenna_switch) { - case 0: break; - case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + case 0: + break; + case 1: + pr_debug("setting GPIO21 to 0 (TV antenna?)\n"); saa7134_set_gpio(dev, 21, 0); break; - case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + case 2: + pr_debug("setting GPIO21 to 1 (Radio antenna?)\n"); saa7134_set_gpio(dev, 21, 1); break; } @@ -587,11 +578,14 @@ static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) struct tda1004x_state *state = fe->demodulator_priv; switch (state->config->antenna_switch) { - case 0: break; - case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + case 0: + break; + case 1: + pr_debug("setting GPIO21 to 1 (Radio antenna?)\n"); saa7134_set_gpio(dev, 21, 1); break; - case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + case 2: + pr_debug("setting GPIO21 to 0 (TV antenna?)\n"); saa7134_set_gpio(dev, 21, 0); break; } @@ -619,7 +613,7 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, &dev->i2c_adap, tuner_conf)) return 0; - wprintk("no tda827x tuner found at addr: %02x\n", + pr_warn("no tda827x tuner found at addr: %02x\n", cdec_conf->tuner_address); } return -EINVAL; @@ -993,7 +987,8 @@ static struct tda10086_config sd1878_4m = { * special case: lnb supply is connected to the gated i2c */ -static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int md8800_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { int res = -EIO; struct saa7134_dev *dev = fe->dvb->priv; @@ -1019,7 +1014,8 @@ static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg) return res; }; -static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int md8800_set_voltage2(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct saa7134_dev *dev = fe->dvb->priv; u8 wbuf[2] = { 0x1f, 00 }; @@ -1041,8 +1037,8 @@ static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg) { - struct saa7134_dev *dev = fe->dvb->priv; - wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__); + pr_warn("%s: sorry can't set high LNB supply voltage from here\n", + __func__); return -EIO; } @@ -1222,10 +1218,10 @@ static int dvb_init(struct saa7134_dev *dev) mutex_init(&dev->frontends.lock); INIT_LIST_HEAD(&dev->frontends.felist); - printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); + pr_info("%s() allocating 1 frontend\n", __func__); fe0 = vb2_dvb_alloc_frontend(&dev->frontends, 1); if (!fe0) { - printk(KERN_ERR "%s() failed to alloc\n", __func__); + pr_err("%s() failed to alloc\n", __func__); return -ENOMEM; } @@ -1250,7 +1246,7 @@ static int dvb_init(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: - dprintk("pinnacle 300i dvb setup\n"); + pr_debug("pinnacle 300i dvb setup\n"); fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, &dev->i2c_adap); if (fe0->dvb.frontend) { @@ -1259,7 +1255,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - dprintk("avertv 777 dvb setup\n"); + pr_debug("avertv 777 dvb setup\n"); fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, &dev->i2c_adap); if (fe0->dvb.frontend) { @@ -1269,7 +1265,7 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_AVERMEDIA_A16D: - dprintk("AverMedia A16D dvb setup\n"); + pr_debug("AverMedia A16D dvb setup\n"); fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_xc3028_mt352_dev, &dev->i2c_adap); @@ -1401,13 +1397,15 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); + pr_warn("%s: Lifeview Trio, No tda826x found!\n", + __func__); goto detach_frontend; } if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0, false) == NULL) { - wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); + pr_warn("%s: Lifeview Trio, No ISL6421 found!\n", + __func__); goto detach_frontend; } } @@ -1422,12 +1420,12 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda827x_attach,fe0->dvb.frontend, ads_tech_duo_config.tuner_address, &dev->i2c_adap, &ads_duo_cfg) == NULL) { - wprintk("no tda827x tuner found at addr: %02x\n", + pr_warn("no tda827x tuner found at addr: %02x\n", ads_tech_duo_config.tuner_address); goto detach_frontend; } } else - wprintk("failed to attach tda10046\n"); + pr_warn("failed to attach tda10046\n"); break; case SAA7134_BOARD_TEVION_DVBT_220RF: if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, @@ -1450,7 +1448,7 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: Medion Quadro, no tda826x " + pr_warn("%s: Medion Quadro, no tda826x " "found !\n", __func__); goto detach_frontend; } @@ -1459,7 +1457,7 @@ static int dvb_init(struct saa7134_dev *dev) fe->ops.i2c_gate_ctrl(fe, 1); if (dvb_attach(isl6405_attach, fe, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - wprintk("%s: Medion Quadro, no ISL6405 " + pr_warn("%s: Medion Quadro, no ISL6405 " "found !\n", __func__); goto detach_frontend; } @@ -1519,13 +1517,13 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: No tda826x found!\n", __func__); + pr_warn("%s: No tda826x found!\n", __func__); goto detach_frontend; } if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0, false) == NULL) { - wprintk("%s: No ISL6421 found!\n", __func__); + pr_warn("%s: No ISL6421 found!\n", __func__); goto detach_frontend; } } @@ -1593,12 +1591,12 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: No tda826x found!\n", __func__); + pr_warn("%s: No tda826x found!\n", __func__); goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { - wprintk("%s: No lnbp21 found!\n", __func__); + pr_warn("%s: No lnbp21 found!\n", __func__); goto detach_frontend; } } @@ -1614,7 +1612,7 @@ static int dvb_init(struct saa7134_dev *dev) goto detach_frontend; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - dprintk("AverMedia E506R dvb setup\n"); + pr_debug("AverMedia E506R dvb setup\n"); saa7134_set_gpio(dev, 25, 0); msleep(10); saa7134_set_gpio(dev, 25, 1); @@ -1630,7 +1628,7 @@ static int dvb_init(struct saa7134_dev *dev) struct dvb_frontend *fe; if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) { - wprintk("%s: MD7134 DVB-S, no SD1878 " + pr_warn("%s: MD7134 DVB-S, no SD1878 " "found !\n", __func__); goto detach_frontend; } @@ -1639,7 +1637,7 @@ static int dvb_init(struct saa7134_dev *dev) fe->ops.i2c_gate_ctrl(fe, 1); if (dvb_attach(isl6405_attach, fe, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - wprintk("%s: MD7134 DVB-S, no ISL6405 " + pr_warn("%s: MD7134 DVB-S, no ISL6405 " "found !\n", __func__); goto detach_frontend; } @@ -1671,15 +1669,15 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: Asus Tiger 3in1, no " + pr_warn("%s: Asus Tiger 3in1, no " "tda826x found!\n", __func__); goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { - wprintk("%s: Asus Tiger 3in1, no lnbp21" + pr_warn("%s: Asus Tiger 3in1, no lnbp21" " found!\n", __func__); - goto detach_frontend; + goto detach_frontend; } } } @@ -1696,13 +1694,13 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: Asus My Cinema PS3-100, no " + pr_warn("%s: Asus My Cinema PS3-100, no " "tda826x found!\n", __func__); goto detach_frontend; } if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, &dev->i2c_adap, 0, 0) == NULL) { - wprintk("%s: Asus My Cinema PS3-100, no lnbp21" + pr_warn("%s: Asus My Cinema PS3-100, no lnbp21" " found!\n", __func__); goto detach_frontend; } @@ -1750,7 +1748,7 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { if (dvb_attach(zl10036_attach, fe0->dvb.frontend, &avertv_a700_tuner, &dev->i2c_adap) == NULL) { - wprintk("%s: No zl10036 found!\n", + pr_warn("%s: No zl10036 found!\n", __func__); } } @@ -1761,7 +1759,7 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) if (dvb_attach(zl10039_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap) == NULL) - wprintk("%s: No zl10039 found!\n", + pr_warn("%s: No zl10039 found!\n", __func__); break; @@ -1774,7 +1772,7 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend, &dev->i2c_adap, &videomate_t750_qt1010_config) == NULL) - wprintk("error attaching QT1010\n"); + pr_warn("error attaching QT1010\n"); } break; case SAA7134_BOARD_ZOLID_HYBRID_PCI: @@ -1850,12 +1848,12 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; if (dvb_attach(zl10039_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap) == NULL) - wprintk("%s: No zl10039 found!\n", + pr_warn("%s: No zl10039 found!\n", __func__); } break; default: - wprintk("Huh? unknown DVB card?\n"); + pr_warn("Huh? unknown DVB card?\n"); break; } @@ -1871,14 +1869,14 @@ static int dvb_init(struct saa7134_dev *dev) fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); if (!fe) { - printk(KERN_ERR "%s/2: xc3028 attach failed\n", + pr_err("%s/2: xc3028 attach failed\n", dev->name); goto detach_frontend; } } if (NULL == fe0->dvb.frontend) { - printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); + pr_err("%s/dvb: frontend initialization failed\n", dev->name); goto detach_frontend; } /* define general-purpose callback pointer */ diff --git a/kernel/drivers/media/pci/saa7134/saa7134-empress.c b/kernel/drivers/media/pci/saa7134/saa7134-empress.c index 594dc3ad4..56b932c97 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-empress.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-empress.c @@ -17,6 +17,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -26,9 +29,6 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); @@ -39,13 +39,6 @@ static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; module_param_array(empress_nr, int, NULL, 0444); MODULE_PARM_DESC(empress_nr,"ts device number"); -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages"); - -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg) - /* ------------------------------------------------------------------ */ static int start_streaming(struct vb2_queue *vq, unsigned int count) @@ -121,11 +114,14 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = video_drvdata(file); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; - saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt); + saa_call_all(dev, pad, get_fmt, NULL, &fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + v4l2_fill_pix_format(&f->fmt.pix, mbus_fmt); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; @@ -137,11 +133,13 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = video_drvdata(file); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); + saa_call_all(dev, pad, set_fmt, NULL, &format); + v4l2_fill_pix_format(&f->fmt.pix, &format.format); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; @@ -154,11 +152,14 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = video_drvdata(file); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - saa_call_all(dev, video, try_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); + saa_call_all(dev, pad, set_fmt, &pad_cfg, &format); + v4l2_fill_pix_format(&f->fmt.pix, &format.format); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; @@ -221,9 +222,9 @@ static void empress_signal_update(struct work_struct *work) container_of(work, struct saa7134_dev, empress_workqueue); if (dev->nosignal) { - dprintk("no video signal\n"); + pr_debug("no video signal\n"); } else { - dprintk("video signal acquired\n"); + pr_debug("video signal acquired\n"); } } @@ -255,7 +256,7 @@ static int empress_init(struct saa7134_dev *dev) struct vb2_queue *q; int err; - dprintk("%s: %s\n",dev->name,__func__); + pr_debug("%s: %s\n", dev->name, __func__); dev->empress_dev = video_device_alloc(); if (NULL == dev->empress_dev) return -ENOMEM; @@ -302,13 +303,13 @@ static int empress_init(struct saa7134_dev *dev) err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, empress_nr[dev->nr]); if (err < 0) { - printk(KERN_INFO "%s: can't register video device\n", + pr_info("%s: can't register video device\n", dev->name); video_device_release(dev->empress_dev); dev->empress_dev = NULL; return err; } - printk(KERN_INFO "%s: registered device %s [mpeg]\n", + pr_info("%s: registered device %s [mpeg]\n", dev->name, video_device_node_name(dev->empress_dev)); empress_signal_update(&dev->empress_workqueue); @@ -317,7 +318,7 @@ static int empress_init(struct saa7134_dev *dev) static int empress_fini(struct saa7134_dev *dev) { - dprintk("%s: %s\n",dev->name,__func__); + pr_debug("%s: %s\n", dev->name, __func__); if (NULL == dev->empress_dev) return 0; diff --git a/kernel/drivers/media/pci/saa7134/saa7134-go7007.c b/kernel/drivers/media/pci/saa7134/saa7134-go7007.c index 54e650b4d..8a2abb341 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-go7007.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-go7007.c @@ -11,6 +11,9 @@ * GNU General Public License for more details. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -27,8 +30,6 @@ #include #include -#include "saa7134.h" -#include "saa7134-reg.h" #include "go7007-priv.h" /*#define GO7007_HPI_DEBUG*/ @@ -288,9 +289,9 @@ static int saa7134_go7007_stream_start(struct go7007 *go) /* Set up transfer block size */ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); - saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); - saa_writeb(SAA7134_TS_DMA1, 0); - saa_writeb(SAA7134_TS_DMA2, 0); + saa_writeb(SAA7134_TS_DMA0, ((PAGE_SIZE >> 7) - 1) & 0xff); + saa_writeb(SAA7134_TS_DMA1, (PAGE_SIZE >> 15) & 0xff); + saa_writeb(SAA7134_TS_DMA2, (PAGE_SIZE >> 31) & 0x3f); /* Enable video streaming mode */ saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); diff --git a/kernel/drivers/media/pci/saa7134/saa7134-i2c.c b/kernel/drivers/media/pci/saa7134/saa7134-i2c.c index f4da674e7..8ef6399d7 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-i2c.c @@ -20,14 +20,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include #include #include -#include "saa7134-reg.h" -#include "saa7134.h" #include /* ----------------------------------------------------------- */ @@ -40,8 +41,15 @@ static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); -#define d1printk if (1 == i2c_debug) printk -#define d2printk if (2 == i2c_debug) printk +#define i2c_dbg(level, fmt, arg...) do { \ + if (i2c_debug == level) \ + printk(KERN_DEBUG pr_fmt("i2c: " fmt), ## arg); \ + } while (0) + +#define i2c_cont(level, fmt, arg...) do { \ + if (i2c_debug == level) \ + pr_cont(fmt, ## arg); \ + } while (0) #define I2C_WAIT_DELAY 32 #define I2C_WAIT_RETRY 16 @@ -89,23 +97,20 @@ static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) enum i2c_status status; status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; - d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name, - str_i2c_status[status]); + i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]); return status; } static inline void i2c_set_status(struct saa7134_dev *dev, enum i2c_status status) { - d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name, - str_i2c_status[status]); + i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]); saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); } static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) { - d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name, - str_i2c_attr[attr]); + i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]); saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); } @@ -168,7 +173,7 @@ static int i2c_reset(struct saa7134_dev *dev) enum i2c_status status; int count; - d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); + i2c_dbg(2, "i2c reset\n"); status = i2c_get_status(dev); if (!i2c_is_error(status)) return true; @@ -206,7 +211,7 @@ static inline int i2c_send_byte(struct saa7134_dev *dev, // dword |= 0x40 << 16; /* 400 kHz */ dword |= 0xf0 << 24; saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); - d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data); + i2c_dbg(2, "i2c data => 0x%x\n", data); if (!i2c_is_busy_wait(dev)) return -EIO; @@ -228,7 +233,7 @@ static inline int i2c_recv_byte(struct saa7134_dev *dev) if (i2c_is_error(status)) return -EIO; data = saa_readb(SAA7134_I2C_DATA); - d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data); + i2c_dbg(2, "i2c data <= 0x%x\n", data); return data; } @@ -245,12 +250,12 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, if (!i2c_reset(dev)) return -EIO; - d2printk("start xfer\n"); - d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name); + i2c_dbg(2, "start xfer\n"); + i2c_dbg(1, "i2c xfer:"); for (i = 0; i < num; i++) { if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { /* send address */ - d2printk("send address\n"); + i2c_dbg(2, "send address\n"); addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) addr |= 1; @@ -262,50 +267,50 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, * needed to talk to the mt352 demux * thanks to pinnacle for the hint */ int quirk = 0xfe; - d1printk(" [%02x quirk]",quirk); + i2c_cont(1, " [%02x quirk]", quirk); i2c_send_byte(dev,START,quirk); i2c_recv_byte(dev); } - d1printk(" < %02x", addr); + i2c_cont(1, " < %02x", addr); rc = i2c_send_byte(dev,START,addr); if (rc < 0) goto err; } if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - d2printk("read bytes\n"); + i2c_dbg(2, "read bytes\n"); for (byte = 0; byte < msgs[i].len; byte++) { - d1printk(" ="); + i2c_cont(1, " ="); rc = i2c_recv_byte(dev); if (rc < 0) goto err; - d1printk("%02x", rc); + i2c_cont(1, "%02x", rc); msgs[i].buf[byte] = rc; } /* discard mysterious extra byte when reading from Samsung S5H1411. i2c bus gets error if we do not. */ if (0x19 == msgs[i].addr) { - d1printk(" ?"); + i2c_cont(1, " ?"); rc = i2c_recv_byte(dev); if (rc < 0) goto err; - d1printk("%02x", rc); + i2c_cont(1, "%02x", rc); } } else { /* write bytes */ - d2printk("write bytes\n"); + i2c_dbg(2, "write bytes\n"); for (byte = 0; byte < msgs[i].len; byte++) { data = msgs[i].buf[byte]; - d1printk(" %02x", data); + i2c_cont(1, " %02x", data); rc = i2c_send_byte(dev,CONTINUE,data); if (rc < 0) goto err; } } } - d2printk("xfer done\n"); - d1printk(" >"); + i2c_dbg(2, "xfer done\n"); + i2c_cont(1, " >"); i2c_set_attr(dev,STOP); rc = -EIO; if (!i2c_is_busy_wait(dev)) @@ -316,12 +321,12 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, /* ensure that the bus is idle for at least one bit slot */ msleep(1); - d1printk("\n"); + i2c_cont(1, "\n"); return num; err: if (1 == i2c_debug) { status = i2c_get_status(dev); - printk(" ERROR: %s\n",str_i2c_status[status]); + i2c_cont(1, " ERROR: %s\n", str_i2c_status[status]); } return rc; } @@ -359,22 +364,22 @@ saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) dev->i2c_client.addr = 0xa0 >> 1; buf = 0; if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { - printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + pr_info("%s: Huh, no eeprom present (err=%d)?\n", dev->name,err); return -1; } if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) { - printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", + pr_warn("%s: i2c eeprom read error (err=%d)\n", dev->name,err); return -1; } - for (i = 0; i < len; i++) { - if (0 == (i % 16)) - printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i); - printk(" %02x",eedata[i]); - if (15 == (i % 16)) - printk("\n"); + + for (i = 0; i < len; i += 16) { + int size = (len - i) > 16 ? 16 : len - i; + + pr_info("i2c eeprom %02x: %*ph\n", i, size, &eedata[i]); } + return 0; } @@ -386,7 +391,7 @@ static char *i2c_devs[128] = { [ 0x5a >> 1 ] = "remote control", }; -static void do_i2c_scan(char *name, struct i2c_client *c) +static void do_i2c_scan(struct i2c_client *c) { unsigned char buf; int i,rc; @@ -396,8 +401,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c) rc = i2c_master_recv(c,&buf,0); if (rc < 0) continue; - printk("%s: i2c scan: found device @ 0x%x [%s]\n", - name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + pr_info("i2c scan: found device @ 0x%x [%s]\n", + i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); } } @@ -415,7 +420,7 @@ int saa7134_i2c_register(struct saa7134_dev *dev) saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); if (i2c_scan) - do_i2c_scan(dev->name,&dev->i2c_client); + do_i2c_scan(&dev->i2c_client); /* Instantiate the IR receiver device, if present */ saa7134_probe_i2c_ir(dev); diff --git a/kernel/drivers/media/pci/saa7134/saa7134-input.c b/kernel/drivers/media/pci/saa7134/saa7134-input.c index dc3d6516e..69d32d3fa 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-input.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-input.c @@ -18,15 +18,15 @@ * */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - #define MODULE_NAME "saa7134" static unsigned int disable_ir; @@ -41,10 +41,14 @@ static int pinnacle_remote; module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); -#define dprintk(fmt, arg...) if (ir_debug) \ - printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg) -#define i2cdprintk(fmt, arg...) if (ir_debug) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg) +#define input_dbg(fmt, arg...) do { \ + if (ir_debug) \ + printk(KERN_DEBUG pr_fmt("input: " fmt), ## arg); \ + } while (0) +#define ir_dbg(ir, fmt, arg...) do { \ + if (ir_debug) \ + printk(KERN_DEBUG pr_fmt("ir %s: " fmt), ir->name, ## arg); \ + } while (0) /* Helper function for raw decoding at GPIO16 or GPIO18 */ static int saa7134_raw_decode_irq(struct saa7134_dev *dev); @@ -75,7 +79,7 @@ static int build_key(struct saa7134_dev *dev) } data = ir_extract_bits(gpio, ir->mask_keycode); - dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", + input_dbg("build_key gpio=0x%x mask=0x%x data=%d\n", gpio, ir->mask_keycode, data); switch (dev->board) { @@ -119,7 +123,7 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_type *protocol, struct saa7134_dev *dev = ir->c->adapter->algo_data; if (dev == NULL) { - i2cdprintk("get_key_flydvb_trio: " + ir_dbg(ir, "get_key_flydvb_trio: " "ir->c->adapter->algo_data is NULL!\n"); return -EIO; } @@ -146,12 +150,12 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_type *protocol, msleep(10); continue; } - i2cdprintk("send wake up byte to pic16C505 (IR chip)" + ir_dbg(ir, "send wake up byte to pic16C505 (IR chip)" "failed %dx\n", attempt); return -EIO; } if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -170,7 +174,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, enum rc_type *protocol /* is needed to access GPIO. Used by the saa_readl macro. */ struct saa7134_dev *dev = ir->c->adapter->algo_data; if (dev == NULL) { - i2cdprintk("get_key_msi_tvanywhere_plus: " + ir_dbg(ir, "get_key_msi_tvanywhere_plus: " "ir->c->adapter->algo_data is NULL!\n"); return -EIO; } @@ -191,7 +195,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, enum rc_type *protocol /* GPIO says there is a button press. Get it. */ if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -202,7 +206,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, enum rc_type *protocol /* Button pressed */ - dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); + input_dbg("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); *protocol = RC_TYPE_UNKNOWN; *scancode = b; *toggle = 0; @@ -219,7 +223,7 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_type *protocol, /* is needed to access GPIO. Used by the saa_readl macro. */ struct saa7134_dev *dev = ir->c->adapter->algo_data; if (dev == NULL) { - i2cdprintk("get_key_kworld_pc150u: " + ir_dbg(ir, "get_key_kworld_pc150u: " "ir->c->adapter->algo_data is NULL!\n"); return -EIO; } @@ -240,7 +244,7 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_type *protocol, /* GPIO says there is a button press. Get it. */ if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -251,7 +255,7 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_type *protocol, /* Button pressed */ - dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); + input_dbg("get_key_kworld_pc150u: Key = 0x%02X\n", b); *protocol = RC_TYPE_UNKNOWN; *scancode = b; *toggle = 0; @@ -265,7 +269,7 @@ static int get_key_purpletv(struct IR_i2c *ir, enum rc_type *protocol, /* poll IR chip */ if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -334,7 +338,7 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_type *protocol, ir->c->addr = 0x5a >> 1; if (12 != i2c_master_recv(ir->c, data, 12)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -359,7 +363,7 @@ static int get_key_pinnacle(struct IR_i2c *ir, enum rc_type *protocol, /* poll IR chip */ if (4 != i2c_master_recv(ir->c, b, 4)) { - i2cdprintk("read error\n"); + ir_dbg(ir, "read error\n"); return -EIO; } @@ -391,7 +395,7 @@ static int get_key_pinnacle(struct IR_i2c *ir, enum rc_type *protocol, *scancode = code; *toggle = 0; - i2cdprintk("Pinnacle PCTV key %02x\n", code); + ir_dbg(ir, "Pinnacle PCTV key %02x\n", code); return 1; } @@ -481,6 +485,7 @@ static int __saa7134_ir_start(void *priv) case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: @@ -629,6 +634,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: @@ -829,10 +835,16 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0xffff; raw_decode = true; break; + case SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM: + ir_codes = RC_MAP_LEADTEK_Y04G0051; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; } if (NULL == ir_codes) { - printk("%s: Oops: IR config error [card=%d]\n", - dev->name, dev->board); + pr_err("Oops: IR config error [card=%d]\n", dev->board); return -ENODEV; } @@ -916,7 +928,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) int rc; if (disable_ir) { - dprintk("IR has been disabled, not probing for i2c remote\n"); + input_dbg("IR has been disabled, not probing for i2c remote\n"); return; } @@ -959,7 +971,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) an existing device. Weird... REVISIT: might no longer be needed */ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); - dprintk("probe 0x%02x @ %s: %s\n", + input_dbg("probe 0x%02x @ %s: %s\n", msg_msi.addr, dev->i2c_adap.name, (1 == rc) ? "yes" : "no"); break; @@ -974,7 +986,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) an existing device. Weird... REVISIT: might no longer be needed */ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); - dprintk("probe 0x%02x @ %s: %s\n", + input_dbg("probe 0x%02x @ %s: %s\n", msg_msi.addr, dev->i2c_adap.name, (1 == rc) ? "yes" : "no"); break; @@ -1019,7 +1031,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) info.addr = 0x0b; break; default: - dprintk("No I2C IR support for board %x\n", dev->board); + input_dbg("No I2C IR support for board %x\n", dev->board); return; } diff --git a/kernel/drivers/media/pci/saa7134/saa7134-ts.c b/kernel/drivers/media/pci/saa7134/saa7134-ts.c index 2709b83d5..7fb5ee7e2 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-ts.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-ts.c @@ -20,23 +20,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - /* ------------------------------------------------------------------ */ static unsigned int ts_debug; module_param(ts_debug, int, 0644); MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); -#define dprintk(fmt, arg...) if (ts_debug) \ - printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg) +#define ts_dbg(fmt, arg...) do { \ + if (ts_debug) \ + printk(KERN_DEBUG pr_fmt("ts: " fmt), ## arg); \ + } while (0) /* ------------------------------------------------------------------ */ static int buffer_activate(struct saa7134_dev *dev, @@ -44,7 +46,7 @@ static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *next) { - dprintk("buffer_activate [%p]",buf); + ts_dbg("buffer_activate [%p]", buf); buf->top_seen = 0; if (!dev->ts_started) @@ -53,12 +55,12 @@ static int buffer_activate(struct saa7134_dev *dev, if (NULL == next) next = buf; if (V4L2_FIELD_TOP == dev->ts_field) { - dprintk("- [top] buf=%p next=%p\n",buf,next); + ts_dbg("- [top] buf=%p next=%p\n", buf, next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); dev->ts_field = V4L2_FIELD_BOTTOM; } else { - dprintk("- [bottom] buf=%p next=%p\n",buf,next); + ts_dbg("- [bottom] buf=%p next=%p\n", buf, next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); dev->ts_field = V4L2_FIELD_TOP; @@ -77,8 +79,9 @@ static int buffer_activate(struct saa7134_dev *dev, int saa7134_ts_buffer_init(struct vb2_buffer *vb2) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); dmaq->curr = NULL; buf->activate = buffer_activate; @@ -89,13 +92,14 @@ EXPORT_SYMBOL_GPL(saa7134_ts_buffer_init); int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; struct saa7134_dev *dev = dmaq->dev; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0); unsigned int lines, llength, size; - dprintk("buffer_prepare [%p]\n", buf); + ts_dbg("buffer_prepare [%p]\n", buf); llength = TS_PACKET_SIZE; lines = dev->ts.nr_packets; @@ -105,14 +109,14 @@ int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2) return -EINVAL; vb2_set_plane_payload(vb2, 0, size); - vb2->v4l2_buf.field = dev->field; + vbuf->field = dev->field; return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents, saa7134_buffer_startpage(buf)); } EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare); -int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +int saa7134_ts_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -146,10 +150,12 @@ int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count) list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) { list_del(&buf->entry); - vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb2.vb2_buf, + VB2_BUF_STATE_QUEUED); } if (dmaq->curr) { - vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&dmaq->curr->vb2.vb2_buf, + VB2_BUF_STATE_QUEUED); dmaq->curr = NULL; } return -EBUSY; @@ -239,7 +245,7 @@ int saa7134_ts_init1(struct saa7134_dev *dev) /* Function for stop TS */ int saa7134_ts_stop(struct saa7134_dev *dev) { - dprintk("TS stop\n"); + ts_dbg("TS stop\n"); if (!dev->ts_started) return 0; @@ -261,7 +267,7 @@ int saa7134_ts_stop(struct saa7134_dev *dev) /* Function for start TS */ int saa7134_ts_start(struct saa7134_dev *dev) { - dprintk("TS start\n"); + ts_dbg("TS start\n"); if (WARN_ON(dev->ts_started)) return 0; diff --git a/kernel/drivers/media/pci/saa7134/saa7134-tvaudio.c b/kernel/drivers/media/pci/saa7134/saa7134-tvaudio.c index 3afbcb70b..21a579309 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -29,9 +32,6 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - /* ------------------------------------------------------------------ */ static unsigned int audio_debug; @@ -49,13 +49,10 @@ static int audio_clock_tweak; module_param(audio_clock_tweak, int, 0644); MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])"); -#define dprintk(fmt, arg...) if (audio_debug) \ - printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg) -#define d2printk(fmt, arg...) if (audio_debug > 1) \ - printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) - -#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \ - dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg))) +#define audio_dbg(level, fmt, arg...) do { \ + if (audio_debug >= level) \ + printk(KERN_DEBUG pr_fmt("audio: " fmt), ## arg); \ + } while (0) /* msecs */ #define SCAN_INITIAL_DELAY 1000 @@ -206,13 +203,14 @@ static void mute_input_7134(struct saa7134_dev *dev) if (dev->hw_mute == mute && dev->hw_input == in && !dev->insuspend) { - dprintk("mute/input: nothing to do [mute=%d,input=%s]\n", - mute,in->name); + audio_dbg(1, "mute/input: nothing to do [mute=%d,input=%s]\n", + mute, in->name); return; } - dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", - dev->ctl_mute,dev->automute,dev->input->name,mute,in->name); + audio_dbg(1, "ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", + dev->ctl_mute, dev->automute, + dev->input->name, mute, in->name); dev->hw_mute = mute; dev->hw_input = in; @@ -265,8 +263,8 @@ static void tvaudio_setmode(struct saa7134_dev *dev, tweak = audio_clock_tweak; if (note) - dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n", - note,audio->name, + audio_dbg(1, "tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n", + note, audio->name, audio->carr1 / 1000, audio->carr1 % 1000, audio->carr2 / 1000, audio->carr2 % 1000, acpf, tweak); @@ -334,14 +332,14 @@ static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) if (!(dev->tvnorm->id & scan->std)) { value = 0; - dprintk("skipping %d.%03d MHz [%4s]\n", - scan->carr / 1000, scan->carr % 1000, scan->name); + audio_dbg(1, "skipping %d.%03d MHz [%4s]\n", + scan->carr / 1000, scan->carr % 1000, scan->name); return 0; } if (audio_debug > 1) { int i; - dprintk("debug %d:",scan->carr); + audio_dbg(1, "debug %d:", scan->carr); for (i = -150; i <= 150; i += 30) { tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i); saa_readl(SAA7134_LEVEL_READOUT1 >> 2); @@ -349,11 +347,11 @@ static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) return -1; value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); if (0 == i) - printk(" # %6d # ",value >> 16); + pr_cont(" # %6d # ", value >> 16); else - printk(" %6d",value >> 16); + pr_cont(" %6d", value >> 16); } - printk("\n"); + pr_cont("\n"); } tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90); @@ -371,9 +369,9 @@ static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) left >>= 16; right >>= 16; value = left > right ? left - right : right - left; - dprintk("scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n", - scan->carr / 1000, scan->carr % 1000, - scan->name, value, left, right); + audio_dbg(1, "scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n", + scan->carr / 1000, scan->carr % 1000, + scan->name, value, left, right); return value; } @@ -389,7 +387,7 @@ static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au case TVAUDIO_FM_K_STEREO: case TVAUDIO_FM_BG_STEREO: idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5; - dprintk("getstereo: fm/stereo: idp=0x%x\n",idp); + audio_dbg(1, "getstereo: fm/stereo: idp=0x%x\n", idp); if (0x03 == (idp & 0x03)) retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; else if (0x05 == (idp & 0x05)) @@ -403,10 +401,11 @@ static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au case TVAUDIO_NICAM_FM: case TVAUDIO_NICAM_AM: nicam = saa_readb(SAA7134_AUDIO_STATUS); - dprintk("getstereo: nicam=0x%x\n",nicam); + audio_dbg(1, "getstereo: nicam=0x%x\n", nicam); if (nicam & 0x1) { nicam_status = saa_readb(SAA7134_NICAM_STATUS); - dprintk("getstereo: nicam_status=0x%x\n", nicam_status); + audio_dbg(1, "getstereo: nicam_status=0x%x\n", + nicam_status); switch (nicam_status & 0x03) { case 0x01: @@ -424,7 +423,7 @@ static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au break; } if (retval != -1) - dprintk("found audio subchannels:%s%s%s%s\n", + audio_dbg(1, "found audio subchannels:%s%s%s%s\n", (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "", (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "", @@ -459,8 +458,8 @@ static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *au case TVAUDIO_FM_BG_STEREO: case TVAUDIO_NICAM_AM: case TVAUDIO_NICAM_FM: - dprintk("setstereo [fm] => %s\n", - name[ mode % ARRAY_SIZE(name) ]); + audio_dbg(1, "setstereo [fm] => %s\n", + name[mode % ARRAY_SIZE(name)]); reg = fm[ mode % ARRAY_SIZE(fm) ]; saa_writeb(SAA7134_FM_DEMATRIX, reg); break; @@ -489,7 +488,8 @@ static int tvaudio_thread(void *data) try_to_freeze(); dev->thread.scan1 = dev->thread.scan2; - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + audio_dbg(1, "tvaudio thread scan start [%d]\n", + dev->thread.scan1); dev->tvaudio = NULL; saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); @@ -519,7 +519,7 @@ static int tvaudio_thread(void *data) if (1 == nscan) { /* only one candidate -- skip scan ;) */ - dprintk("only one main carrier candidate - skipping scan\n"); + audio_dbg(1, "only one main carrier candidate - skipping scan\n"); max1 = 12345; carrier = default_carrier; } else { @@ -544,26 +544,24 @@ static int tvaudio_thread(void *data) if (0 != carrier && max1 > 2000 && max1 > max2*3) { /* found good carrier */ - dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n", - dev->tvnorm->name, carrier/1000, carrier%1000, - max1, max2); + audio_dbg(1, "found %s main sound carrier @ %d.%03d MHz [%d/%d]\n", + dev->tvnorm->name, carrier/1000, carrier%1000, + max1, max2); dev->last_carrier = carrier; dev->automute = 0; } else if (0 != dev->last_carrier) { /* no carrier -- try last detected one as fallback */ carrier = dev->last_carrier; - dprintk("audio carrier scan failed, " - "using %d.%03d MHz [last detected]\n", - carrier/1000, carrier%1000); + audio_dbg(1, "audio carrier scan failed, using %d.%03d MHz [last detected]\n", + carrier/1000, carrier%1000); dev->automute = 1; } else { /* no carrier + no fallback -- use default */ carrier = default_carrier; - dprintk("audio carrier scan failed, " - "using %d.%03d MHz [default]\n", - carrier/1000, carrier%1000); + audio_dbg(1, "audio carrier scan failed, using %d.%03d MHz [default]\n", + carrier/1000, carrier%1000); dev->automute = 1; } tvaudio_setcarrier(dev,carrier,carrier); @@ -661,7 +659,7 @@ static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev) { int state = saa_readb(SAA7135_DSP_RWSTATE); if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { - d2printk("%s: resetting error bit\n", dev->name); + audio_dbg(2, "%s: resetting error bit\n", dev->name); saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR); } return 0; @@ -673,18 +671,17 @@ static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) state = saa_readb(SAA7135_DSP_RWSTATE); if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { - printk(KERN_WARNING "%s: dsp access error\n", dev->name); + pr_warn("%s: dsp access error\n", dev->name); saa_dsp_reset_error_bit(dev); return -EIO; } while (0 == (state & bit)) { if (unlikely(0 == count)) { - printk("%s: dsp access wait timeout [bit=%s]\n", - dev->name, - (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" : - (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" : - (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" : - "???"); + pr_err("dsp access wait timeout [bit=%s]\n", + (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" : + (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" : + (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" : + "???"); return -EIO; } saa_wait(DSP_DELAY); @@ -699,7 +696,7 @@ int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value) { int err; - d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value); + audio_dbg(2, "dsp write reg 0x%x = 0x%06x\n", reg << 2, value); err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); if (err < 0) return err; @@ -786,14 +783,16 @@ static int tvaudio_thread_ddep(void *data) try_to_freeze(); dev->thread.scan1 = dev->thread.scan2; - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + audio_dbg(1, "tvaudio thread scan start [%d]\n", + dev->thread.scan1); if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { /* insmod option override */ norms = (audio_ddep << 2) | 0x01; - dprintk("ddep override: %s\n",stdres[audio_ddep]); + audio_dbg(1, "ddep override: %s\n", + stdres[audio_ddep]); } else if (&card(dev).radio == dev->input) { - dprintk("FM Radio\n"); + audio_dbg(1, "FM Radio\n"); if (dev->tuner_type == TUNER_PHILIPS_TDA8290) { norms = (0x11 << 2) | 0x01; /* set IF frequency to 5.5 MHz */ @@ -816,12 +815,12 @@ static int tvaudio_thread_ddep(void *data) norms |= 0x10; if (0 == norms) norms = 0x7c; /* all */ - dprintk("scanning:%s%s%s%s%s\n", - (norms & 0x04) ? " B/G" : "", - (norms & 0x08) ? " D/K" : "", - (norms & 0x10) ? " L/L'" : "", - (norms & 0x20) ? " I" : "", - (norms & 0x40) ? " M" : ""); + audio_dbg(1, "scanning:%s%s%s%s%s\n", + (norms & 0x04) ? " B/G" : "", + (norms & 0x08) ? " D/K" : "", + (norms & 0x10) ? " L/L'" : "", + (norms & 0x20) ? " I" : "", + (norms & 0x40) ? " M" : ""); } /* kick automatic standard detection */ @@ -836,29 +835,28 @@ static int tvaudio_thread_ddep(void *data) goto restart; value = saa_readl(0x528 >> 2) & 0xffffff; - dprintk("tvaudio thread status: 0x%x [%s%s%s]\n", - value, stdres[value & 0x1f], - (value & 0x000020) ? ",stereo" : "", - (value & 0x000040) ? ",dual" : ""); - dprintk("detailed status: " - "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", - (value & 0x000080) ? " A2/EIAJ pilot tone " : "", - (value & 0x000100) ? " A2/EIAJ dual " : "", - (value & 0x000200) ? " A2/EIAJ stereo " : "", - (value & 0x000400) ? " A2/EIAJ noise mute " : "", - - (value & 0x000800) ? " BTSC/FM radio pilot " : "", - (value & 0x001000) ? " SAP carrier " : "", - (value & 0x002000) ? " BTSC stereo noise mute " : "", - (value & 0x004000) ? " SAP noise mute " : "", - (value & 0x008000) ? " VDSP " : "", - - (value & 0x010000) ? " NICST " : "", - (value & 0x020000) ? " NICDU " : "", - (value & 0x040000) ? " NICAM muted " : "", - (value & 0x080000) ? " NICAM reserve sound " : "", - - (value & 0x100000) ? " init done " : ""); + audio_dbg(1, "tvaudio thread status: 0x%x [%s%s%s]\n", + value, stdres[value & 0x1f], + (value & 0x000020) ? ",stereo" : "", + (value & 0x000040) ? ",dual" : ""); + audio_dbg(1, "detailed status: %s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (value & 0x000080) ? " A2/EIAJ pilot tone " : "", + (value & 0x000100) ? " A2/EIAJ dual " : "", + (value & 0x000200) ? " A2/EIAJ stereo " : "", + (value & 0x000400) ? " A2/EIAJ noise mute " : "", + + (value & 0x000800) ? " BTSC/FM radio pilot " : "", + (value & 0x001000) ? " SAP carrier " : "", + (value & 0x002000) ? " BTSC stereo noise mute " : "", + (value & 0x004000) ? " SAP noise mute " : "", + (value & 0x008000) ? " VDSP " : "", + + (value & 0x010000) ? " NICST " : "", + (value & 0x020000) ? " NICDU " : "", + (value & 0x040000) ? " NICAM muted " : "", + (value & 0x080000) ? " NICAM reserve sound " : "", + + (value & 0x100000) ? " init done " : ""); } done: @@ -1031,7 +1029,7 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) /* start tvaudio thread */ dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); if (IS_ERR(dev->thread.thread)) { - printk(KERN_WARNING "%s: kernel_thread() failed\n", + pr_warn("%s: kernel_thread() failed\n", dev->name); /* XXX: missing error handling here */ } @@ -1061,7 +1059,7 @@ int saa7134_tvaudio_fini(struct saa7134_dev *dev) int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) { if (dev->input->amux != TV) { - dprintk("sound IF not in use, skipping scan\n"); + audio_dbg(1, "sound IF not in use, skipping scan\n"); dev->automute = 0; saa7134_tvaudio_setmute(dev); } else if (dev->thread.thread) { diff --git a/kernel/drivers/media/pci/saa7134/saa7134-vbi.c b/kernel/drivers/media/pci/saa7134/saa7134-vbi.c index 5306e549e..6271b0eb0 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-vbi.c @@ -20,14 +20,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - /* ------------------------------------------------------------------ */ static unsigned int vbi_debug; @@ -38,8 +38,10 @@ static unsigned int vbibufs = 4; module_param(vbibufs, int, 0444); MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); -#define dprintk(fmt, arg...) if (vbi_debug) \ - printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg) +#define vbi_dbg(fmt, arg...) do { \ + if (vbi_debug) \ + printk(KERN_DEBUG pr_fmt("vbi: " fmt), ## arg); \ + } while (0) /* ------------------------------------------------------------------ */ @@ -81,10 +83,10 @@ static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next) { - struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv; + struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_buf.vb2_queue->drv_priv; unsigned long control, base; - dprintk("buffer_activate [%p]\n", buf); + vbi_dbg("buffer_activate [%p]\n", buf); buf->top_seen = 0; task_init(dev, buf, TASK_A); @@ -117,8 +119,9 @@ static int buffer_prepare(struct vb2_buffer *vb2) { struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; struct saa7134_dev *dev = dmaq->dev; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); - struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0); unsigned int size; if (dma->sgl->offset) { @@ -135,7 +138,7 @@ static int buffer_prepare(struct vb2_buffer *vb2) saa7134_buffer_startpage(buf)); } -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -159,7 +162,8 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, static int buffer_init(struct vb2_buffer *vb2) { struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); dmaq->curr = NULL; buf->activate = buffer_activate; diff --git a/kernel/drivers/media/pci/saa7134/saa7134-video.c b/kernel/drivers/media/pci/saa7134/saa7134-video.c index 99d09a756..518086c7a 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134-video.c +++ b/kernel/drivers/media/pci/saa7134/saa7134-video.c @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "saa7134.h" +#include "saa7134-reg.h" + #include #include #include @@ -31,9 +34,6 @@ #include #include -#include "saa7134-reg.h" -#include "saa7134.h" - /* ------------------------------------------------------------------ */ unsigned int video_debug; @@ -52,8 +52,10 @@ module_param_string(secam, secam, sizeof(secam), 0644); MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc"); -#define dprintk(fmt, arg...) if (video_debug&0x04) \ - printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg) +#define video_dbg(fmt, arg...) do { \ + if (video_debug & 0x04) \ + printk(KERN_DEBUG pr_fmt("video: " fmt), ## arg); \ + } while (0) /* ------------------------------------------------------------------ */ /* Defines for Video Output Port Register at address 0x191 */ @@ -385,7 +387,7 @@ static struct saa7134_format* format_by_fourcc(unsigned int fourcc) static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) { - dprintk("set tv norm = %s\n",norm->name); + video_dbg("set tv norm = %s\n", norm->name); dev->tvnorm = norm; /* setup cropping */ @@ -407,7 +409,7 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) static void video_mux(struct saa7134_dev *dev, int input) { - dprintk("video input = %d [%s]\n", input, card_in(dev, input).name); + video_dbg("video input = %d [%s]\n", input, card_in(dev, input).name); dev->ctl_input = input; set_tvnorm(dev, dev->tvnorm); saa7134_tvaudio_setinput(dev, &card_in(dev, input)); @@ -531,14 +533,14 @@ static void set_v_scale(struct saa7134_dev *dev, int task, int yscale) mirror = (dev->ctl_mirror) ? 0x02 : 0x00; if (yscale < 2048) { /* LPI */ - dprintk("yscale LPI yscale=%d\n",yscale); + video_dbg("yscale LPI yscale=%d\n", yscale); saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror); saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40); saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40); } else { /* ACM */ val = 0x40 * 1024 / yscale; - dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val); + video_dbg("yscale ACM yscale=%d val=0x%x\n", yscale, val); saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror); saa_writeb(SAA7134_LUMA_CONTRAST(task), val); saa_writeb(SAA7134_CHROMA_SATURATION(task), val); @@ -573,7 +575,8 @@ static void set_size(struct saa7134_dev *dev, int task, prescale = 1; xscale = 1024 * dev->crop_current.width / prescale / width; yscale = 512 * div * dev->crop_current.height / height; - dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale); + video_dbg("prescale=%d xscale=%d yscale=%d\n", + prescale, xscale, yscale); set_h_prescale(dev,task,prescale); saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff); saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8); @@ -615,7 +618,7 @@ static void set_cliplist(struct saa7134_dev *dev, int reg, saa_writeb(reg + 0, winbits); saa_writeb(reg + 2, cl[i].position & 0xff); saa_writeb(reg + 3, cl[i].position >> 8); - dprintk("clip: %s winbits=%02x pos=%d\n", + video_dbg("clip: %s winbits=%02x pos=%d\n", name,winbits,cl[i].position); reg += 8; } @@ -730,7 +733,7 @@ static int start_preview(struct saa7134_dev *dev) return err; dev->ovfield = dev->win.field; - dprintk("start_preview %dx%d+%d+%d %s field=%s\n", + video_dbg("start_preview %dx%d+%d+%d %s field=%s\n", dev->win.w.width, dev->win.w.height, dev->win.w.left, dev->win.w.top, dev->ovfmt->name, v4l2_field_names[dev->ovfield]); @@ -788,11 +791,11 @@ static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next) { - struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv; + struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_buf.vb2_queue->drv_priv; unsigned long base,control,bpl; unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ - dprintk("buffer_activate buf=%p\n",buf); + video_dbg("buffer_activate buf=%p\n", buf); buf->top_seen = 0; set_size(dev, TASK_A, dev->width, dev->height, @@ -837,7 +840,7 @@ static int buffer_activate(struct saa7134_dev *dev, base3 = base2 + bpl_uv * lines_uv; if (dev->fmt->uvswap) tmp = base2, base2 = base3, base3 = tmp; - dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", + video_dbg("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", bpl_uv,lines_uv,base2,base3); if (V4L2_FIELD_HAS_BOTH(dev->field)) { /* interlaced */ @@ -869,7 +872,8 @@ static int buffer_activate(struct saa7134_dev *dev, static int buffer_init(struct vb2_buffer *vb2) { struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); dmaq->curr = NULL; buf->activate = buffer_activate; @@ -880,8 +884,9 @@ static int buffer_prepare(struct vb2_buffer *vb2) { struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; struct saa7134_dev *dev = dmaq->dev; - struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); - struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0); unsigned int size; if (dma->sgl->offset) { @@ -893,13 +898,13 @@ static int buffer_prepare(struct vb2_buffer *vb2) return -EINVAL; vb2_set_plane_payload(vb2, 0, size); - vb2->v4l2_buf.field = dev->field; + vbuf->field = dev->field; return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents, saa7134_buffer_startpage(buf)); } -static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -929,7 +934,8 @@ void saa7134_vb2_buffer_queue(struct vb2_buffer *vb) { struct saa7134_dmaqueue *dmaq = vb->vb2_queue->drv_priv; struct saa7134_dev *dev = dmaq->dev; - struct saa7134_buf *buf = container_of(vb, struct saa7134_buf, vb2); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); saa7134_buffer_queue(dev, dmaq, buf); } @@ -950,10 +956,12 @@ int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) { list_del(&buf->entry); - vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb2.vb2_buf, + VB2_BUF_STATE_QUEUED); } if (dmaq->curr) { - vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&dmaq->curr->vb2.vb2_buf, + VB2_BUF_STATE_QUEUED); dmaq->curr = NULL; } return -EBUSY; @@ -1229,7 +1237,7 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, int i; if (saa7134_no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } f->fmt.win = dev->win; @@ -1305,7 +1313,7 @@ static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, struct saa7134_dev *dev = video_drvdata(file); if (saa7134_no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } @@ -1339,7 +1347,7 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, unsigned long flags; if (saa7134_no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } if (f->fmt.win.clips == NULL) @@ -1738,7 +1746,7 @@ static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (saa7134_no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } @@ -1795,7 +1803,7 @@ static int saa7134_overlay(struct file *file, void *priv, unsigned int on) if (on) { if (saa7134_no_overlay > 0) { - dprintk("no_overlay\n"); + video_dbg("no_overlay\n"); return -EINVAL; } @@ -2184,7 +2192,7 @@ void saa7134_irq_video_signalchange(struct saa7134_dev *dev) st1 = saa_readb(SAA7134_STATUS_VIDEO1); st2 = saa_readb(SAA7134_STATUS_VIDEO2); - dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", + video_dbg("DCSDT: pll: %s, sync: %s, norm: %s\n", (st1 & 0x40) ? "not locked" : "locked", (st2 & 0x40) ? "no" : "yes", st[st1 & 0x03]); diff --git a/kernel/drivers/media/pci/saa7134/saa7134.h b/kernel/drivers/media/pci/saa7134/saa7134.h index 8bf0553b8..6b6d234f5 100644 --- a/kernel/drivers/media/pci/saa7134/saa7134.h +++ b/kernel/drivers/media/pci/saa7134/saa7134.h @@ -21,6 +21,8 @@ #define SAA7134_VERSION "0, 2, 17" +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -339,6 +341,8 @@ struct saa7134_card_ir { #define SAA7134_BOARD_HAWELL_HW_9004V1 191 #define SAA7134_BOARD_AVERMEDIA_A706 192 #define SAA7134_BOARD_WIS_VOYAGER 193 +#define SAA7134_BOARD_AVERMEDIA_505 194 +#define SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM 195 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -456,7 +460,7 @@ struct saa7134_thread { /* buffer for one video/vbi/ts frame */ struct saa7134_buf { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb2; + struct vb2_v4l2_buffer vb2; /* saa7134 specific */ unsigned int top_seen; @@ -654,7 +658,8 @@ struct saa7134_dev { /* SAA7134_MPEG_DVB only */ struct vb2_dvb_frontends frontends; int (*original_demod_sleep)(struct dvb_frontend *fe); - int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*original_set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); #endif void (*gate_ctrl)(struct saa7134_dev *dev, int open); @@ -815,7 +820,7 @@ void saa7134_video_fini(struct saa7134_dev *dev); int saa7134_ts_buffer_init(struct vb2_buffer *vb2); int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2); -int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +int saa7134_ts_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]); int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count); diff --git a/kernel/drivers/media/pci/saa7164/Kconfig b/kernel/drivers/media/pci/saa7164/Kconfig index a53db7d1c..9098ef5fe 100644 --- a/kernel/drivers/media/pci/saa7164/Kconfig +++ b/kernel/drivers/media/pci/saa7164/Kconfig @@ -5,7 +5,6 @@ config VIDEO_SAA7164 select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEOBUF_DVB select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT diff --git a/kernel/drivers/media/pci/saa7164/saa7164-api.c b/kernel/drivers/media/pci/saa7164/saa7164-api.c index 4f3b1dd18..e7e586c1b 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-api.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-api.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -1373,7 +1373,8 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, u8 buf[256]; int ret; - dprintk(DBGLVL_API, "%s()\n", __func__); + dprintk(DBGLVL_API, "%s() addr=%x reglen=%d datalen=%d\n", + __func__, addr, reglen, datalen); if (reglen > 4) return -EIO; @@ -1434,7 +1435,8 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, u8 buf[256]; int ret; - dprintk(DBGLVL_API, "%s()\n", __func__); + dprintk(DBGLVL_API, "%s() addr=0x%2x len=0x%x\n", + __func__, addr, datalen); if ((datalen == 0) || (datalen > 232)) return -EIO; @@ -1464,7 +1466,8 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, return -EIO; } - dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); + dprintk(DBGLVL_API, "%s() len = %d bytes unitid=0x%x\n", __func__, + len, unitid); /* Prepare the send buffer */ /* Bytes 00-03 dest register length diff --git a/kernel/drivers/media/pci/saa7164/saa7164-buffer.c b/kernel/drivers/media/pci/saa7164/saa7164-buffer.c index 9bd1f73f8..f30758e24 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-buffer.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-buffer.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-bus.c b/kernel/drivers/media/pci/saa7164/saa7164-bus.c index 6c73f5b15..a18fe5d47 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-bus.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-bus.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-cards.c b/kernel/drivers/media/pci/saa7164/saa7164-cards.c index 5b72da5ce..c2b738227 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-cards.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-cards.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -30,6 +30,7 @@ * attached I2C devices, so we can simplify the virtual i2c mechansms * and keep the -i2c.c implementation clean. */ +#define REGLEN_0bit 0 #define REGLEN_8bit 1 #define REGLEN_16bit 2 @@ -499,6 +500,144 @@ struct saa7164_board saa7164_boards[] = { .i2c_reg_len = REGLEN_8bit, } }, }, + [SAA7164_BOARD_HAUPPAUGE_HVR2255proto] = { + .name = "Hauppauge WinTV-HVR2255(proto)", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x27, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xb2 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x24, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x26, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x1c >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2255] = { + .name = "Hauppauge WinTV-HVR2255", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x28, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-1", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xb2 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x25, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x27, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "LGDT3306-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x1c >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, + [SAA7164_BOARD_HAUPPAUGE_HVR2205] = { + .name = "Hauppauge WinTV-HVR2205", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x28, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-1", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x06, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "SI2168-1", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc8 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x25, + .type = SAA7164_UNIT_TUNER, + .name = "SI2157-2", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_0bit, + }, { + .id = 0x27, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "SI2168-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xcc >> 1, + .i2c_reg_len = REGLEN_0bit, + } }, + }, }; const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); @@ -546,6 +685,21 @@ struct saa7164_subid saa7164_subids[] = { .subvendor = 0x0070, .subdevice = 0x8953, .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5, + }, { + .subvendor = 0x0070, + .subdevice = 0xf111, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2255, + /* Prototype card left here for documenation purposes. + .card = SAA7164_BOARD_HAUPPAUGE_HVR2255proto, + */ + }, { + .subvendor = 0x0070, + .subdevice = 0xf123, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2205, + }, { + .subvendor = 0x0070, + .subdevice = 0xf120, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2205, }, }; const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); @@ -594,12 +748,26 @@ void saa7164_gpio_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + case SAA7164_BOARD_HAUPPAUGE_HVR2205: /* + HVR2200 / HVR2250 GPIO 2: s5h1411 / tda10048-1 demod reset GPIO 3: s5h1411 / tda10048-2 demod reset GPIO 7: IRBlaster Zilog reset */ + /* HVR2255 + * GPIO 2: lgdg3306-1 demod reset + * GPIO 3: lgdt3306-2 demod reset + */ + + /* HVR2205 + * GPIO 2: si2168-1 demod reset + * GPIO 3: si2168-2 demod reset + */ + /* Reset parts by going in and out of reset */ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); @@ -647,6 +815,21 @@ static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) /* WinTV-HVR2200 (PCIe, Retail, half-height) * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ break; + case 151009: + /* First production board rev B2I6 */ + /* WinTV-HVR2205 (PCIe, Retail, full-height bracket) + * DVB-T/T2/C (SI2157/SI2168) and basic analog, FM */ + break; + case 151609: + /* First production board rev B2I6 */ + /* WinTV-HVR2205 (PCIe, Retail, half-height bracket) + * DVB-T/T2/C (SI2157/SI2168) and basic analog, FM */ + break; + case 151061: + /* First production board rev B1I6 */ + /* WinTV-HVR2255 (PCIe, Retail, full-height bracket) + * ATSC/QAM (SI2157/LGDT3306) and basic analog, FM */ + break; default: printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", dev->name, tv.model); @@ -676,6 +859,9 @@ void saa7164_card_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + case SAA7164_BOARD_HAUPPAUGE_HVR2205: hauppauge_eeprom(dev, &eeprom[0]); break; } diff --git a/kernel/drivers/media/pci/saa7164/saa7164-cmd.c b/kernel/drivers/media/pci/saa7164/saa7164-cmd.c index cfabcbacc..3285c37b4 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-cmd.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-cmd.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-core.c b/kernel/drivers/media/pci/saa7164/saa7164-core.c index 9cf3c6cba..8bbd092fb 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-core.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-core.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -85,6 +85,11 @@ module_param(guard_checking, int, 0644); MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns"); +static bool enable_msi = true; +module_param(enable_msi, bool, 0444); +MODULE_PARM_DESC(enable_msi, + "enable the use of an msi interrupt if available"); + static unsigned int saa7164_devcount; static DEFINE_MUTEX(devlist); @@ -618,12 +623,7 @@ static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) static irqreturn_t saa7164_irq(int irq, void *dev_id) { struct saa7164_dev *dev = dev_id; - struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1]; - struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2]; - struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1]; - struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2]; - struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1]; - struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2]; + struct saa7164_port *porta, *portb, *portc, *portd, *porte, *portf; u32 intid, intstat[INT_SIZE/4]; int i, handled = 0, bit; @@ -634,6 +634,13 @@ static irqreturn_t saa7164_irq(int irq, void *dev_id) goto out; } + porta = &dev->ports[SAA7164_PORT_TS1]; + portb = &dev->ports[SAA7164_PORT_TS2]; + portc = &dev->ports[SAA7164_PORT_ENC1]; + portd = &dev->ports[SAA7164_PORT_ENC2]; + porte = &dev->ports[SAA7164_PORT_VBI1]; + portf = &dev->ports[SAA7164_PORT_VBI2]; + /* Check that the hardware is accessible. If the status bytes are * 0xFF then the device is not accessible, the the IRQ belongs * to another driver. @@ -1184,6 +1191,39 @@ static int saa7164_thread_function(void *data) return 0; } +static bool saa7164_enable_msi(struct pci_dev *pci_dev, struct saa7164_dev *dev) +{ + int err; + + if (!enable_msi) { + printk(KERN_WARNING "%s() MSI disabled by module parameter 'enable_msi'" + , __func__); + return false; + } + + err = pci_enable_msi(pci_dev); + + if (err) { + printk(KERN_ERR "%s() Failed to enable MSI interrupt." + " Falling back to a shared IRQ\n", __func__); + return false; + } + + /* no error - so request an msi interrupt */ + err = request_irq(pci_dev->irq, saa7164_irq, 0, + dev->name, dev); + + if (err) { + /* fall back to legacy interrupt */ + printk(KERN_ERR "%s() Failed to get an MSI interrupt." + " Falling back to a shared IRQ\n", __func__); + pci_disable_msi(pci_dev); + return false; + } + + return true; +} + static int saa7164_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { @@ -1224,19 +1264,28 @@ static int saa7164_initdev(struct pci_dev *pci_dev, pci_set_master(pci_dev); /* TODO */ - if (!pci_dma_supported(pci_dev, 0xffffffff)) { + err = pci_set_dma_mask(pci_dev, 0xffffffff); + if (err) { printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); - err = -EIO; goto fail_irq; } - err = request_irq(pci_dev->irq, saa7164_irq, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, - pci_dev->irq); - err = -EIO; - goto fail_irq; + /* irq bit */ + if (saa7164_enable_msi(pci_dev, dev)) { + dev->msi = true; + } else { + /* if we have an error (i.e. we don't have an interrupt) + or msi is not enabled - fallback to shared interrupt */ + + err = request_irq(pci_dev->irq, saa7164_irq, + IRQF_SHARED, dev->name, dev); + + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, + pci_dev->irq); + err = -EIO; + goto fail_irq; + } } pci_set_drvdata(pci_dev, dev); @@ -1439,6 +1488,11 @@ static void saa7164_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); + if (dev->msi) { + pci_disable_msi(pci_dev); + dev->msi = false; + } + pci_disable_device(pci_dev); mutex_lock(&devlist); diff --git a/kernel/drivers/media/pci/saa7164/saa7164-dvb.c b/kernel/drivers/media/pci/saa7164/saa7164-dvb.c index 16ae71592..e9a783b71 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-dvb.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-dvb.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -24,6 +24,9 @@ #include "tda10048.h" #include "tda18271.h" #include "s5h1411.h" +#include "si2157.h" +#include "si2168.h" +#include "lgdt3306a.h" #define DRIVER_NAME "saa7164" @@ -82,6 +85,65 @@ static struct s5h1411_config hauppauge_s5h1411_config = { .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct lgdt3306a_config hauppauge_hvr2255a_config = { + .i2c_addr = 0xb2 >> 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + +static struct lgdt3306a_config hauppauge_hvr2255b_config = { + .i2c_addr = 0x1c >> 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + +static struct si2157_config hauppauge_hvr2255_tuner_config = { + .inversion = 1, + .if_port = 1, +}; + +static int si2157_attach(struct saa7164_port *port, struct i2c_adapter *adapter, + struct dvb_frontend *fe, u8 addr8bit, struct si2157_config *cfg) +{ + struct i2c_board_info bi; + struct i2c_client *tuner; + + cfg->fe = fe; + + memset(&bi, 0, sizeof(bi)); + + strlcpy(bi.type, "si2157", I2C_NAME_SIZE); + bi.platform_data = cfg; + bi.addr = addr8bit >> 1; + + request_module(bi.type); + + tuner = i2c_new_device(adapter, &bi); + if (tuner == NULL || tuner->dev.driver == NULL) + return -ENODEV; + + if (!try_module_get(tuner->dev.driver->owner)) { + i2c_unregister_device(tuner); + return -ENODEV; + } + + port->i2c_client_tuner = tuner; + + return 0; +} + static int saa7164_dvb_stop_port(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; @@ -242,14 +304,16 @@ static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - mutex_lock(&dvb->lock); - if (dvb->feeding++ == 0) { - /* Start transport */ - ret = saa7164_dvb_start_port(port); + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + ret = saa7164_dvb_start_port(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); return ret; } @@ -264,14 +328,16 @@ static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); - mutex_lock(&dvb->lock); - if (--dvb->feeding == 0) { - /* Stop transport */ - ret = saa7164_dvb_stop_streaming(port); + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + ret = saa7164_dvb_stop_streaming(port); + } + mutex_unlock(&dvb->lock); + dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", + __func__, port->nr, dvb->feeding); } - mutex_unlock(&dvb->lock); - dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", - __func__, port->nr, dvb->feeding); return ret; } @@ -425,6 +491,7 @@ int saa7164_dvb_unregister(struct saa7164_port *port) struct saa7164_dev *dev = port->dev; struct saa7164_buffer *b; struct list_head *c, *n; + struct i2c_client *client; dprintk(DBGLVL_DVB, "%s()\n", __func__); @@ -443,6 +510,20 @@ int saa7164_dvb_unregister(struct saa7164_port *port) if (dvb->frontend == NULL) return 0; + /* remove I2C client for tuner */ + client = port->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C client for demodulator */ + client = port->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); @@ -462,6 +543,12 @@ int saa7164_dvb_register(struct saa7164_port *port) struct saa7164_dev *dev = port->dev; struct saa7164_dvb *dvb = &port->dvb; struct saa7164_i2c *i2c_bus = NULL; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + struct i2c_adapter *adapter; + struct i2c_board_info info; + struct i2c_client *client_demod; + struct i2c_client *client_tuner; int ret; dprintk(DBGLVL_DVB, "%s()\n", __func__); @@ -527,6 +614,126 @@ int saa7164_dvb_register(struct saa7164_port *port) } } + break; + case SAA7164_BOARD_HAUPPAUGE_HVR2255proto: + case SAA7164_BOARD_HAUPPAUGE_HVR2255: + i2c_bus = &dev->i2c_bus[2]; + + if (port->nr == 0) { + port->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_hvr2255a_config, &i2c_bus->i2c_adap); + } else { + port->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_hvr2255b_config, &i2c_bus->i2c_adap); + } + + if (port->dvb.frontend != NULL) { + + if (port->nr == 0) { + si2157_attach(port, &dev->i2c_bus[0].i2c_adap, + port->dvb.frontend, 0xc0, + &hauppauge_hvr2255_tuner_config); + } else { + si2157_attach(port, &dev->i2c_bus[1].i2c_adap, + port->dvb.frontend, 0xc0, + &hauppauge_hvr2255_tuner_config); + } + } + break; + case SAA7164_BOARD_HAUPPAUGE_HVR2205: + + if (port->nr == 0) { + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &port->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0xc8 >> 1; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&dev->i2c_bus[2].i2c_adap, + &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.if_port = 1; + si2157_config.fe = port->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0xc0 >> 1; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[0].i2c_adap, + &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + } else { + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &port->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0xcc >> 1; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&dev->i2c_bus[2].i2c_adap, + &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = port->dvb.frontend; + si2157_config.if_port = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0xc0 >> 1; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, + &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + } + break; default: printk(KERN_ERR "%s: The frontend isn't supported\n", @@ -548,5 +755,9 @@ int saa7164_dvb_register(struct saa7164_port *port) } return 0; + +frontend_detach: + printk(KERN_ERR "%s() Frontend/I2C initialization failed\n", __func__); + return -1; } diff --git a/kernel/drivers/media/pci/saa7164/saa7164-encoder.c b/kernel/drivers/media/pci/saa7164/saa7164-encoder.c index 7a0a65146..1b184c39b 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-encoder.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -25,6 +25,18 @@ #define ENCODER_MIN_BITRATE 1000000 #define ENCODER_DEF_BITRATE 5000000 +/* + * This is a dummy non-zero value for the sizeimage field of v4l2_pix_format. + * It is not actually used for anything since this driver does not support + * stream I/O, only read(), and because this driver produces an MPEG stream + * and not discrete frames. But the V4L2 spec doesn't allow for this value + * to be 0, so set it to 0x10000 instead. + * + * If we ever change this driver to support stream I/O, then this field + * will be the size of the streaming buffers. + */ +#define SAA7164_SIZEIMAGE (0x10000) + static struct saa7164_tvnorm saa7164_tvnorms[] = { { .name = "NTSC-M", @@ -35,24 +47,6 @@ static struct saa7164_tvnorm saa7164_tvnorms[] = { } }; -static const u32 saa7164_v4l2_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_SHARPNESS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_B_FRAMES, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_AUDIO_MUTE, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_CID_MPEG_VIDEO_BITRATE, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0 -}; - /* Take the encoder configuration form the port struct and * flush it to the hardware. */ @@ -211,10 +205,8 @@ static int saa7164_encoder_initialize(struct saa7164_port *port) } /* -- V4L2 --------------------------------------------------------- */ -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) +int saa7164_s_std(struct saa7164_port *port, v4l2_std_id id) { - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; struct saa7164_dev *dev = port->dev; unsigned int i; @@ -240,22 +232,33 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) return 0; } -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; + return saa7164_s_std(fh->port, id); +} + +int saa7164_g_std(struct saa7164_port *port, v4l2_std_id *id) +{ *id = port->std; return 0; } -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - int n; + struct saa7164_encoder_fh *fh = file->private_data; + + return saa7164_g_std(fh->port, id); +} - char *inputs[] = { "tuner", "composite", "svideo", "aux", - "composite 2", "svideo 2", "aux 2" }; +int saa7164_enum_input(struct file *file, void *priv, struct v4l2_input *i) +{ + static const char * const inputs[] = { + "tuner", "composite", "svideo", "aux", + "composite 2", "svideo 2", "aux 2" + }; + int n; if (i->index >= 7) return -EINVAL; @@ -273,10 +276,8 @@ static int vidioc_enum_input(struct file *file, void *priv, return 0; } -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +int saa7164_g_input(struct saa7164_port *port, unsigned int *i) { - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; struct saa7164_dev *dev = port->dev; if (saa7164_api_get_videomux(port) != SAA_OK) @@ -289,10 +290,15 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; + + return saa7164_g_input(fh->port, i); +} + +int saa7164_s_input(struct saa7164_port *port, unsigned int i) +{ struct saa7164_dev *dev = port->dev; dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i); @@ -308,8 +314,14 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct saa7164_encoder_fh *fh = file->private_data; + + return saa7164_s_input(fh->port, i); +} + +int saa7164_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct saa7164_encoder_fh *fh = file->private_data; struct saa7164_port *port = fh->port; @@ -319,38 +331,45 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "tuner"); - t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; + t->rangelow = SAA7164_TV_MIN_FREQ; + t->rangehigh = SAA7164_TV_MAX_FREQ; dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type); return 0; } -static int vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *t) +int saa7164_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) { + if (0 != t->index) + return -EINVAL; + /* Update the A/V core */ return 0; } -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +int saa7164_g_frequency(struct saa7164_port *port, struct v4l2_frequency *f) { - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; + if (f->tuner) + return -EINVAL; - f->type = V4L2_TUNER_ANALOG_TV; f->frequency = port->freq; - return 0; } -static int vidioc_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) { struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; + + return saa7164_g_frequency(fh->port, f); +} + +int saa7164_s_frequency(struct saa7164_port *port, + const struct v4l2_frequency *f) +{ struct saa7164_dev *dev = port->dev; struct saa7164_port *tsport; struct dvb_frontend *fe; @@ -370,16 +389,13 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0) return -EINVAL; - if (f->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - - port->freq = f->frequency; + port->freq = clamp(f->frequency, + SAA7164_TV_MIN_FREQ, SAA7164_TV_MAX_FREQ); /* Update the hardware */ if (port->nr == SAA7164_PORT_ENC1) tsport = &dev->ports[SAA7164_PORT_TS1]; - else - if (port->nr == SAA7164_PORT_ENC2) + else if (port->nr == SAA7164_PORT_ENC2) tsport = &dev->ports[SAA7164_PORT_TS2]; else BUG(); @@ -396,253 +412,54 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) { struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, - ctl->id, ctl->value); - - switch (ctl->id) { - case V4L2_CID_BRIGHTNESS: - ctl->value = port->ctl_brightness; - break; - case V4L2_CID_CONTRAST: - ctl->value = port->ctl_contrast; - break; - case V4L2_CID_SATURATION: - ctl->value = port->ctl_saturation; - break; - case V4L2_CID_HUE: - ctl->value = port->ctl_hue; - break; - case V4L2_CID_SHARPNESS: - ctl->value = port->ctl_sharpness; - break; - case V4L2_CID_AUDIO_VOLUME: - ctl->value = port->ctl_volume; - break; - default: - return -EINVAL; - } - return 0; + return saa7164_s_frequency(fh->port, f); } -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) +static int saa7164_s_ctrl(struct v4l2_ctrl *ctrl) { - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; + struct saa7164_port *port = + container_of(ctrl->handler, struct saa7164_port, ctrl_handler); + struct saa7164_encoder_params *params = &port->encoder_params; int ret = 0; - dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, - ctl->id, ctl->value); - - switch (ctl->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_brightness = ctl->value; - saa7164_api_set_usercontrol(port, - PU_BRIGHTNESS_CONTROL); - } else - ret = -EINVAL; + port->ctl_brightness = ctrl->val; + saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL); break; case V4L2_CID_CONTRAST: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_contrast = ctl->value; - saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); - } else - ret = -EINVAL; + port->ctl_contrast = ctrl->val; + saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); break; case V4L2_CID_SATURATION: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_saturation = ctl->value; - saa7164_api_set_usercontrol(port, - PU_SATURATION_CONTROL); - } else - ret = -EINVAL; + port->ctl_saturation = ctrl->val; + saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL); break; case V4L2_CID_HUE: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_hue = ctl->value; - saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); - } else - ret = -EINVAL; + port->ctl_hue = ctrl->val; + saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); break; case V4L2_CID_SHARPNESS: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_sharpness = ctl->value; - saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); - } else - ret = -EINVAL; + port->ctl_sharpness = ctrl->val; + saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); break; case V4L2_CID_AUDIO_VOLUME: - if ((ctl->value >= -83) && (ctl->value <= 24)) { - port->ctl_volume = ctl->value; - saa7164_api_set_audio_volume(port, port->ctl_volume); - } else - ret = -EINVAL; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int saa7164_get_ctrl(struct saa7164_port *port, - struct v4l2_ext_control *ctrl) -{ - struct saa7164_encoder_params *params = &port->encoder_params; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = params->bitrate; - break; - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = params->stream_type; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - ctrl->value = params->ctl_mute; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - ctrl->value = params->ctl_aspect; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - ctrl->value = params->bitrate_mode; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - ctrl->value = params->refdist; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - ctrl->value = params->bitrate_peak; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = params->gop_size; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_get_ctrl(port, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - -static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) -{ - int ret = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE: - if ((ctrl->value >= ENCODER_MIN_BITRATE) && - (ctrl->value <= ENCODER_MAX_BITRATE)) - ret = 0; + port->ctl_volume = ctrl->val; + saa7164_api_set_audio_volume(port, port->ctl_volume); break; - case V4L2_CID_MPEG_STREAM_TYPE: - if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || - (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) - ret = 0; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - if ((ctrl->value >= 0) && - (ctrl->value <= 1)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && - (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if ((ctrl->value >= 0) && - (ctrl->value <= 255)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) || - (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - if ((ctrl->value >= 1) && - (ctrl->value <= 3)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - if ((ctrl->value >= ENCODER_MIN_BITRATE) && - (ctrl->value <= ENCODER_MAX_BITRATE)) - ret = 0; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_try_ctrl(ctrl, 0); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - - return -EINVAL; -} - -static int saa7164_set_ctrl(struct saa7164_port *port, - struct v4l2_ext_control *ctrl) -{ - struct saa7164_encoder_params *params = &port->encoder_params; - int ret = 0; - - switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_BITRATE: - params->bitrate = ctrl->value; + params->bitrate = ctrl->val; break; case V4L2_CID_MPEG_STREAM_TYPE: - params->stream_type = ctrl->value; + params->stream_type = ctrl->val; break; case V4L2_CID_MPEG_AUDIO_MUTE: - params->ctl_mute = ctrl->value; + params->ctl_mute = ctrl->val; ret = saa7164_api_audio_mute(port, params->ctl_mute); if (ret != SAA_OK) { printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, @@ -651,7 +468,7 @@ static int saa7164_set_ctrl(struct saa7164_port *port, } break; case V4L2_CID_MPEG_VIDEO_ASPECT: - params->ctl_aspect = ctrl->value; + params->ctl_aspect = ctrl->val; ret = saa7164_api_set_aspect_ratio(port); if (ret != SAA_OK) { printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, @@ -660,55 +477,24 @@ static int saa7164_set_ctrl(struct saa7164_port *port, } break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - params->bitrate_mode = ctrl->value; + params->bitrate_mode = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: - params->refdist = ctrl->value; + params->refdist = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - params->bitrate_peak = ctrl->value; + params->bitrate_peak = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - params->gop_size = ctrl->value; + params->gop_size = ctrl->val; break; default: - return -EINVAL; + ret = -EINVAL; } - /* TODO: Update the hardware */ - return ret; } -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_try_ctrl(ctrl, 0); - if (err) { - ctrls->error_idx = i; - break; - } - err = saa7164_set_ctrl(port, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -745,145 +531,22 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, +static int vidioc_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7164_encoder_fh *fh = file->private_data; struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; + f->fmt.pix.sizeimage = SAA7164_SIZEIMAGE; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.width = port->width; f->fmt.pix.height = port->height; - - dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n", - port->width, port->height); - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; - dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n", - port->width, port->height); return 0; } -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; - - dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - - return 0; -} - -static int fill_queryctrl(struct saa7164_encoder_params *params, - struct v4l2_queryctrl *c) -{ - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(c, - ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, - 100000, ENCODER_DEF_BITRATE); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(c, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_TS, - 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(c, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, - 1, V4L2_MPEG_VIDEO_ASPECT_4x3); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return v4l2_ctrl_query_fill(c, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, - 1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(c, - 1, 3, 1, 1); - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - return v4l2_ctrl_query_fill(c, - ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, - 100000, ENCODER_DEF_BITRATE); - default: - return -EINVAL; - } -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct saa7164_encoder_fh *fh = priv; - struct saa7164_port *port = fh->port; - int i, next; - u32 id = c->id; - - memset(c, 0, sizeof(*c)); - - next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); - c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; - - for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { - if (next) { - if (c->id < saa7164_v4l2_ctrls[i]) - c->id = saa7164_v4l2_ctrls[i]; - else - continue; - } - - if (c->id == saa7164_v4l2_ctrls[i]) - return fill_queryctrl(&port->encoder_params, c); - - if (c->id < saa7164_v4l2_ctrls[i]) - break; - } - - return -EINVAL; -} - static int saa7164_encoder_stop_port(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; @@ -1084,8 +747,10 @@ static int fops_open(struct file *file) if (NULL == fh) return -ENOMEM; - file->private_data = fh; fh->port = port; + v4l2_fh_init(&fh->fh, video_devdata(file)); + v4l2_fh_add(&fh->fh); + file->private_data = fh; return 0; } @@ -1106,7 +771,8 @@ static int fops_release(struct file *file) } } - file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); return 0; @@ -1250,10 +916,11 @@ err: static unsigned int fops_poll(struct file *file, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct saa7164_encoder_fh *fh = (struct saa7164_encoder_fh *)file->private_data; struct saa7164_port *port = fh->port; - unsigned int mask = 0; + unsigned int mask = v4l2_ctrl_poll(file, wait); port->last_poll_msecs_diff = port->last_poll_msecs; port->last_poll_msecs = jiffies_to_msecs(jiffies); @@ -1263,26 +930,18 @@ static unsigned int fops_poll(struct file *file, poll_table *wait) saa7164_histogram_update(&port->poll_interval, port->last_poll_msecs_diff); - if (!video_is_registered(port->v4l_device)) - return -EIO; + if (!(req_events & (POLLIN | POLLRDNORM))) + return mask; if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { if (atomic_inc_return(&port->v4l_reader_count) == 1) { if (saa7164_encoder_initialize(port) < 0) - return -EINVAL; + return mask | POLLERR; saa7164_encoder_start_streaming(port); msleep(200); } } - /* blocking wait for buffer */ - if ((file->f_flags & O_NONBLOCK) == 0) { - if (wait_event_interruptible(port->wait_read, - saa7164_enc_next_buf(port))) { - return -ERESTARTSYS; - } - } - /* Pull the first buffer from the used list */ if (!list_empty(&port->list_buf_used.list)) mask |= POLLIN | POLLRDNORM; @@ -1290,6 +949,10 @@ static unsigned int fops_poll(struct file *file, poll_table *wait) return mask; } +static const struct v4l2_ctrl_ops saa7164_ctrl_ops = { + .s_ctrl = saa7164_s_ctrl, +}; + static const struct v4l2_file_operations mpeg_fops = { .owner = THIS_MODULE, .open = fops_open, @@ -1302,24 +965,21 @@ static const struct v4l2_file_operations mpeg_fops = { static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, + .vidioc_enum_input = saa7164_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = saa7164_g_tuner, + .vidioc_s_tuner = saa7164_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_fmt_vid_cap = vidioc_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device saa7164_mpeg_template = { @@ -1357,6 +1017,7 @@ static struct video_device *saa7164_encoder_alloc( int saa7164_encoder_register(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; + struct v4l2_ctrl_handler *hdl = &port->ctrl_handler; int result = -ENODEV; dprintk(DBGLVL_ENC, "%s()\n", __func__); @@ -1381,19 +1042,52 @@ int saa7164_encoder_register(struct saa7164_port *port) port->video_format = EU_VIDEO_FORMAT_MPEG_2; port->audio_format = 0; port->video_resolution = 0; - port->ctl_brightness = 127; - port->ctl_contrast = 66; - port->ctl_hue = 128; - port->ctl_saturation = 62; - port->ctl_sharpness = 8; - port->encoder_params.bitrate = ENCODER_DEF_BITRATE; - port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE; - port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS; - port->encoder_params.ctl_mute = 0; - port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3; - port->encoder_params.refdist = 1; - port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE; + port->freq = SAA7164_TV_MIN_FREQ; + + v4l2_ctrl_handler_init(hdl, 14); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 66); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 62); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_SHARPNESS, 0x0, 0x0f, 1, 8); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_AUDIO_MUTE, 0x0, 0x01, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -83, 24, 1, 20); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, + 100000, ENCODER_DEF_BITRATE); + v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 0, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_221x100, 0, + V4L2_MPEG_VIDEO_ASPECT_4x3); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, 15); + v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 1, 3, 1, 1); + v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, + 100000, ENCODER_DEF_BITRATE); + if (hdl->error) { + result = hdl->error; + goto failed; + } + port->std = V4L2_STD_NTSC_M; if (port->encodernorm.id & V4L2_STD_525_60) @@ -1412,6 +1106,8 @@ int saa7164_encoder_register(struct saa7164_port *port) goto failed; } + port->v4l_device->ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); video_set_drvdata(port->v4l_device, port); result = video_register_device(port->v4l_device, VFL_TYPE_GRABBER, -1); @@ -1466,6 +1162,7 @@ void saa7164_encoder_unregister(struct saa7164_port *port) port->v4l_device = NULL; } + v4l2_ctrl_handler_free(&port->ctrl_handler); dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); } diff --git a/kernel/drivers/media/pci/saa7164/saa7164-fw.c b/kernel/drivers/media/pci/saa7164/saa7164-fw.c index add06ab51..269e0782c 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-fw.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-fw.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-i2c.c b/kernel/drivers/media/pci/saa7164/saa7164-i2c.c index 4f7e3b422..0342d8491 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-i2c.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -39,9 +39,10 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x len = 0x%x\n", __func__, num, msgs[i].addr, msgs[i].len); if (msgs[i].flags & I2C_M_RD) { - /* Unsupported - Yet*/ - printk(KERN_ERR "%s() Unsupported - Yet\n", __func__); - continue; + retval = saa7164_api_i2c_read(bus, + msgs[i].addr, + 0 /* reglen */, + NULL /* reg */, msgs[i].len, msgs[i].buf); } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && msgs[i].addr == msgs[i + 1].addr) { /* write then read from same address */ diff --git a/kernel/drivers/media/pci/saa7164/saa7164-reg.h b/kernel/drivers/media/pci/saa7164/saa7164-reg.h index 2bbf81583..37521a2ee 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-reg.h +++ b/kernel/drivers/media/pci/saa7164/saa7164-reg.h @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-types.h b/kernel/drivers/media/pci/saa7164/saa7164-types.h index f48ba978f..1efba6c64 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-types.h +++ b/kernel/drivers/media/pci/saa7164/saa7164-types.h @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 diff --git a/kernel/drivers/media/pci/saa7164/saa7164-vbi.c b/kernel/drivers/media/pci/saa7164/saa7164-vbi.c index 06117e6c0..ee5449145 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/kernel/drivers/media/pci/saa7164/saa7164-vbi.c @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -21,20 +21,6 @@ #include "saa7164.h" -static struct saa7164_tvnorm saa7164_tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - }, { - .name = "NTSC-JP", - .id = V4L2_STD_NTSC_M_JP, - } -}; - -static const u32 saa7164_v4l2_ctrls[] = { - 0 -}; - /* Take the encoder configuration from the port struct and * flush it to the hardware. */ @@ -43,23 +29,13 @@ static void saa7164_vbi_configure(struct saa7164_port *port) struct saa7164_dev *dev = port->dev; dprintk(DBGLVL_VBI, "%s()\n", __func__); - port->vbi_params.width = port->width; - port->vbi_params.height = port->height; + port->vbi_params.width = port->enc_port->width; + port->vbi_params.height = port->enc_port->height; port->vbi_params.is_50hz = - (port->encodernorm.id & V4L2_STD_625_50) != 0; + (port->enc_port->encodernorm.id & V4L2_STD_625_50) != 0; /* Set up the DIF (enable it) for analog mode by default */ saa7164_api_initialize_dif(port); - - /* Configure the correct video standard */ -#if 0 - saa7164_api_configure_dif(port, port->encodernorm.id); -#endif - -#if 0 - /* Ensure the audio decoder is correct configured */ - saa7164_api_set_audio_std(port); -#endif dprintk(DBGLVL_VBI, "%s() ends\n", __func__); } @@ -186,468 +162,50 @@ static int saa7164_vbi_initialize(struct saa7164_port *port) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - unsigned int i; - dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)id); - - for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { - if (id & saa7164_tvnorms[i].id) - break; - } - if (i == ARRAY_SIZE(saa7164_tvnorms)) - return -EINVAL; - - port->encodernorm = saa7164_tvnorms[i]; - port->std = id; - - /* Update the audio decoder while is not running in - * auto detect mode. - */ - saa7164_api_set_audio_std(port); - - dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)id); - - return 0; + return saa7164_s_std(fh->port->enc_port, id); } static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { struct saa7164_encoder_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - *id = port->std; - return 0; -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - int n; - - char *inputs[] = { "tuner", "composite", "svideo", "aux", - "composite 2", "svideo 2", "aux 2" }; - - if (i->index >= 7) - return -EINVAL; - - strcpy(i->name, inputs[i->index]); - - if (i->index == 0) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; - - for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++) - i->std |= saa7164_tvnorms[n].id; - - return 0; + return saa7164_g_std(fh->port->enc_port, id); } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - if (saa7164_api_get_videomux(port) != SAA_OK) - return -EIO; - - *i = (port->mux_input - 1); - - dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i); - return 0; + return saa7164_g_input(fh->port->enc_port, i); } static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i); - - if (i >= 7) - return -EINVAL; - - port->mux_input = i + 1; - if (saa7164_api_set_videomux(port) != SAA_OK) - return -EIO; - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - if (0 != t->index) - return -EINVAL; - - strcpy(t->name, "tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; - - dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type); - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *t) -{ - /* Update the A/V core */ - return 0; + return saa7164_s_input(fh->port->enc_port, i); } static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - f->type = V4L2_TUNER_ANALOG_TV; - f->frequency = port->freq; - - return 0; + return saa7164_g_frequency(fh->port->enc_port, f); } static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - struct saa7164_port *tsport; - struct dvb_frontend *fe; - - /* TODO: Pull this for the std */ - struct analog_parameters params = { - .mode = V4L2_TUNER_ANALOG_TV, - .audmode = V4L2_TUNER_MODE_STEREO, - .std = port->encodernorm.id, - .frequency = f->frequency - }; - - /* Stop the encoder */ - dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__, - f->frequency, f->tuner); - - if (f->tuner != 0) - return -EINVAL; - - if (f->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - - port->freq = f->frequency; - - /* Update the hardware */ - if (port->nr == SAA7164_PORT_VBI1) - tsport = &dev->ports[SAA7164_PORT_TS1]; - else - if (port->nr == SAA7164_PORT_VBI2) - tsport = &dev->ports[SAA7164_PORT_TS2]; - else - BUG(); - - fe = tsport->dvb.frontend; - - if (fe && fe->ops.tuner_ops.set_analog_params) - fe->ops.tuner_ops.set_analog_params(fe, ¶ms); - else - printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); - - saa7164_vbi_initialize(port); - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, - ctl->id, ctl->value); - - switch (ctl->id) { - case V4L2_CID_BRIGHTNESS: - ctl->value = port->ctl_brightness; - break; - case V4L2_CID_CONTRAST: - ctl->value = port->ctl_contrast; - break; - case V4L2_CID_SATURATION: - ctl->value = port->ctl_saturation; - break; - case V4L2_CID_HUE: - ctl->value = port->ctl_hue; - break; - case V4L2_CID_SHARPNESS: - ctl->value = port->ctl_sharpness; - break; - case V4L2_CID_AUDIO_VOLUME: - ctl->value = port->ctl_volume; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - int ret = 0; - - dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, - ctl->id, ctl->value); - - switch (ctl->id) { - case V4L2_CID_BRIGHTNESS: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_brightness = ctl->value; - saa7164_api_set_usercontrol(port, - PU_BRIGHTNESS_CONTROL); - } else - ret = -EINVAL; - break; - case V4L2_CID_CONTRAST: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_contrast = ctl->value; - saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); - } else - ret = -EINVAL; - break; - case V4L2_CID_SATURATION: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_saturation = ctl->value; - saa7164_api_set_usercontrol(port, - PU_SATURATION_CONTROL); - } else - ret = -EINVAL; - break; - case V4L2_CID_HUE: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_hue = ctl->value; - saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); - } else - ret = -EINVAL; - break; - case V4L2_CID_SHARPNESS: - if ((ctl->value >= 0) && (ctl->value <= 255)) { - port->ctl_sharpness = ctl->value; - saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); - } else - ret = -EINVAL; - break; - case V4L2_CID_AUDIO_VOLUME: - if ((ctl->value >= -83) && (ctl->value <= 24)) { - port->ctl_volume = ctl->value; - saa7164_api_set_audio_volume(port, port->ctl_volume); - } else - ret = -EINVAL; - break; - default: - ret = -EINVAL; - } + int ret = saa7164_s_frequency(fh->port->enc_port, f); + if (ret == 0) + saa7164_vbi_initialize(fh->port); return ret; } -static int saa7164_get_ctrl(struct saa7164_port *port, - struct v4l2_ext_control *ctrl) -{ - struct saa7164_vbi_params *params = &port->vbi_params; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - ctrl->value = params->stream_type; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - ctrl->value = params->ctl_mute; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - ctrl->value = params->ctl_aspect; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - ctrl->value = params->refdist; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = params->gop_size; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_get_ctrl(port, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - -static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) -{ - int ret = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || - (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) - ret = 0; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - if ((ctrl->value >= 0) && - (ctrl->value <= 1)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && - (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if ((ctrl->value >= 0) && - (ctrl->value <= 255)) - ret = 0; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - if ((ctrl->value >= 1) && - (ctrl->value <= 3)) - ret = 0; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_try_ctrl(ctrl, 0); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - } - - return -EINVAL; -} - -static int saa7164_set_ctrl(struct saa7164_port *port, - struct v4l2_ext_control *ctrl) -{ - struct saa7164_vbi_params *params = &port->vbi_params; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - params->stream_type = ctrl->value; - break; - case V4L2_CID_MPEG_AUDIO_MUTE: - params->ctl_mute = ctrl->value; - ret = saa7164_api_audio_mute(port, params->ctl_mute); - if (ret != SAA_OK) { - printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, - ret); - ret = -EIO; - } - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - params->ctl_aspect = ctrl->value; - ret = saa7164_api_set_aspect_ratio(port); - if (ret != SAA_OK) { - printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, - ret); - ret = -EIO; - } - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - params->refdist = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - params->gop_size = ctrl->value; - break; - default: - return -EINVAL; - } - - /* TODO: Update the hardware */ - - return ret; -} - -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - int i, err = 0; - - if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = ctrls->controls + i; - - err = saa7164_try_ctrl(ctrl, 0); - if (err) { - ctrls->error_idx = i; - break; - } - err = saa7164_set_ctrl(port, ctrl); - if (err) { - ctrls->error_idx = i; - break; - } - } - return err; - - } - - return -EINVAL; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -672,144 +230,6 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index != 0) - return -EINVAL; - - strlcpy(f->description, "VBI", sizeof(f->description)); - f->pixelformat = V4L2_PIX_FMT_MPEG; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; - f->fmt.pix.width = port->width; - f->fmt.pix.height = port->height; - - dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n", - port->width, port->height); - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; - dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n", - port->width, port->height); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct saa7164_vbi_fh *fh = file->private_data; - struct saa7164_port *port = fh->port; - struct saa7164_dev *dev = port->dev; - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - port->ts_packet_size * port->ts_packet_count; - f->fmt.pix.colorspace = 0; - - dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - - return 0; -} - -static int fill_queryctrl(struct saa7164_vbi_params *params, - struct v4l2_queryctrl *c) -{ - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(c, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_TS, - 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(c, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, - 1, V4L2_MPEG_VIDEO_ASPECT_4x3); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(c, - 1, 3, 1, 1); - default: - return -EINVAL; - } -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct saa7164_vbi_fh *fh = priv; - struct saa7164_port *port = fh->port; - int i, next; - u32 id = c->id; - - memset(c, 0, sizeof(*c)); - - next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); - c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; - - for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { - if (next) { - if (c->id < saa7164_v4l2_ctrls[i]) - c->id = saa7164_v4l2_ctrls[i]; - else - continue; - } - - if (c->id == saa7164_v4l2_ctrls[i]) - return fill_queryctrl(&port->vbi_params, c); - - if (c->id < saa7164_v4l2_ctrls[i]) - break; - } - - return -EINVAL; -} - static int saa7164_vbi_stop_port(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; @@ -999,7 +419,6 @@ static int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f) { /* ntsc */ - f->fmt.vbi.samples_per_line = 1600; f->fmt.vbi.samples_per_line = 1440; f->fmt.vbi.sampling_rate = 27000000; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -1009,6 +428,7 @@ static int saa7164_vbi_fmt(struct file *file, void *priv, f->fmt.vbi.count[0] = 18; f->fmt.vbi.start[1] = 263 + 10 + 1; f->fmt.vbi.count[1] = 18; + memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); return 0; } @@ -1031,8 +451,10 @@ static int fops_open(struct file *file) if (NULL == fh) return -ENOMEM; - file->private_data = fh; fh->port = port; + v4l2_fh_init(&fh->fh, video_devdata(file)); + v4l2_fh_add(&fh->fh); + file->private_data = fh; return 0; } @@ -1053,7 +475,8 @@ static int fops_release(struct file *file) } } - file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); return 0; @@ -1248,24 +671,14 @@ static const struct v4l2_file_operations vbi_fops = { static const struct v4l2_ioctl_ops vbi_ioctl_ops = { .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, + .vidioc_enum_input = saa7164_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = saa7164_g_tuner, + .vidioc_s_tuner = saa7164_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, - .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt, .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt, .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt, @@ -1335,7 +748,7 @@ int saa7164_vbi_register(struct saa7164_port *port) goto failed; } - port->std = V4L2_STD_NTSC_M; + port->enc_port = &dev->ports[port->nr - 2]; video_set_drvdata(port->v4l_device, port); result = video_register_device(port->v4l_device, VFL_TYPE_VBI, -1); diff --git a/kernel/drivers/media/pci/saa7164/saa7164.h b/kernel/drivers/media/pci/saa7164/saa7164.h index cd1a07ce2..8337524bf 100644 --- a/kernel/drivers/media/pci/saa7164/saa7164.h +++ b/kernel/drivers/media/pci/saa7164/saa7164.h @@ -1,7 +1,7 @@ /* * Driver for the NXP SAA7164 PCIe bridge * - * Copyright (c) 2010 Steven Toth + * Copyright (c) 2010-2015 Steven Toth * * 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 @@ -54,8 +54,6 @@ #include #include -#include -#include #include #include #include @@ -64,6 +62,8 @@ #include #include #include +#include +#include #include "saa7164-reg.h" #include "saa7164-types.h" @@ -83,6 +83,9 @@ #define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 #define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9 #define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10 +#define SAA7164_BOARD_HAUPPAUGE_HVR2255proto 11 +#define SAA7164_BOARD_HAUPPAUGE_HVR2255 12 +#define SAA7164_BOARD_HAUPPAUGE_HVR2205 13 #define SAA7164_MAX_UNITS 8 #define SAA7164_TS_NUMBER_OF_LINES 312 @@ -114,7 +117,11 @@ #define DBGLVL_CPU 8192 #define SAA7164_NORMS \ - (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443) + (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP) + +/* TV frequency range copied from tuner-core.c */ +#define SAA7164_TV_MIN_FREQ (44U * 16U) +#define SAA7164_TV_MAX_FREQ (958U * 16U) enum port_t { SAA7164_MPEG_UNDEFINED = 0, @@ -182,11 +189,13 @@ struct saa7164_subid { }; struct saa7164_encoder_fh { + struct v4l2_fh fh; struct saa7164_port *port; atomic_t v4l_reading; }; struct saa7164_vbi_fh { + struct v4l2_fh fh; struct saa7164_port *port; atomic_t v4l_reading; }; @@ -371,17 +380,18 @@ struct saa7164_port { /* --- DVB Transport Specific --- */ struct saa7164_dvb dvb; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; /* --- Encoder/V4L related attributes --- */ /* Encoder */ /* Defaults established in saa7164-encoder.c */ struct saa7164_tvnorm encodernorm; + struct v4l2_ctrl_handler ctrl_handler; v4l2_std_id std; u32 height; u32 width; u32 freq; - u32 ts_packet_size; - u32 ts_packet_count; u8 mux_input; u8 encoder_profile; u8 video_format; @@ -414,6 +424,7 @@ struct saa7164_port { /* V4L VBI */ struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc; struct saa7164_vbi_params vbi_params; + struct saa7164_port *enc_port; /* Debug */ u32 sync_errors; @@ -459,6 +470,7 @@ struct saa7164_dev { /* Interrupt status and ack registers */ u32 int_status; u32 int_ack; + bool msi; struct cmd cmds[SAA_CMD_MAX_MSG_UNITS]; struct mutex lock; @@ -588,6 +600,16 @@ extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i); /* ----------------------------------------------------------- */ /* saa7164-encoder.c */ +int saa7164_s_std(struct saa7164_port *port, v4l2_std_id id); +int saa7164_g_std(struct saa7164_port *port, v4l2_std_id *id); +int saa7164_enum_input(struct file *file, void *priv, struct v4l2_input *i); +int saa7164_g_input(struct saa7164_port *port, unsigned int *i); +int saa7164_s_input(struct saa7164_port *port, unsigned int i); +int saa7164_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t); +int saa7164_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t); +int saa7164_g_frequency(struct saa7164_port *port, struct v4l2_frequency *f); +int saa7164_s_frequency(struct saa7164_port *port, + const struct v4l2_frequency *f); int saa7164_encoder_register(struct saa7164_port *port); void saa7164_encoder_unregister(struct saa7164_port *port); diff --git a/kernel/drivers/media/pci/smipcie/Kconfig b/kernel/drivers/media/pci/smipcie/Kconfig index 21a1583db..c11c77283 100644 --- a/kernel/drivers/media/pci/smipcie/Kconfig +++ b/kernel/drivers/media/pci/smipcie/Kconfig @@ -7,6 +7,7 @@ config DVB_SMIPCIE select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + depends on RC_CORE help Support for cards with SMI PCIe bridge: - DVBSky S950 V3 diff --git a/kernel/drivers/media/pci/smipcie/Makefile b/kernel/drivers/media/pci/smipcie/Makefile index be55481a6..013bc3fe4 100644 --- a/kernel/drivers/media/pci/smipcie/Makefile +++ b/kernel/drivers/media/pci/smipcie/Makefile @@ -1,3 +1,6 @@ + +smipcie-objs := smipcie-main.o smipcie-ir.o + obj-$(CONFIG_DVB_SMIPCIE) += smipcie.o ccflags-y += -Idrivers/media/tuners diff --git a/kernel/drivers/media/pci/smipcie/smipcie-ir.c b/kernel/drivers/media/pci/smipcie/smipcie-ir.c new file mode 100644 index 000000000..d018673c7 --- /dev/null +++ b/kernel/drivers/media/pci/smipcie/smipcie-ir.c @@ -0,0 +1,232 @@ +/* + * SMI PCIe driver for DVBSky cards. + * + * Copyright (C) 2014 Max nibble + * + * 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 "smipcie.h" + +static void smi_ir_enableInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_ENA_SET, IR_X_INT); +} + +static void smi_ir_disableInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_ENA_CLR, IR_X_INT); +} + +static void smi_ir_clearInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_STATUS_CLR, IR_X_INT); +} + +static void smi_ir_stop(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_ir_disableInterrupt(ir); + smi_clear(IR_Init_Reg, 0x80); +} + +#define BITS_PER_COMMAND 14 +#define GROUPS_PER_BIT 2 +#define IR_RC5_MIN_BIT 36 +#define IR_RC5_MAX_BIT 52 +static u32 smi_decode_rc5(u8 *pData, u8 size) +{ + u8 index, current_bit, bit_count; + u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4]; + u8 group_index = 0; + u32 command = 0xFFFFFFFF; + + group_array[group_index++] = 1; + + for (index = 0; index < size; index++) { + + current_bit = (pData[index] & 0x80) ? 1 : 0; + bit_count = pData[index] & 0x7f; + + if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) { + goto process_code; + } else if ((bit_count >= IR_RC5_MIN_BIT) && + (bit_count <= IR_RC5_MAX_BIT)) { + group_array[group_index++] = current_bit; + } else if ((bit_count > IR_RC5_MAX_BIT) && + (bit_count <= 2*IR_RC5_MAX_BIT)) { + group_array[group_index++] = current_bit; + group_array[group_index++] = current_bit; + } else { + goto invalid_timing; + } + if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT) + goto process_code; + + if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1) + && (group_array[group_index-1] == 0)) { + group_array[group_index++] = 1; + goto process_code; + } + } + +process_code: + if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) + group_array[group_index++] = 1; + + if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) { + command = 0; + for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); + index = index + 2) { + if ((group_array[index] == 1) && + (group_array[index+1] == 0)) { + command |= (1 << (BITS_PER_COMMAND - + (index/2) - 1)); + } else if ((group_array[index] == 0) && + (group_array[index+1] == 1)) { + /* */ + } else { + command = 0xFFFFFFFF; + goto invalid_timing; + } + } + } + +invalid_timing: + return command; +} + +static void smi_ir_decode(struct work_struct *work) +{ + struct smi_rc *ir = container_of(work, struct smi_rc, work); + struct smi_dev *dev = ir->dev; + struct rc_dev *rc_dev = ir->rc_dev; + u32 dwIRControl, dwIRData, dwIRCode, scancode; + u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle; + + dwIRControl = smi_read(IR_Init_Reg); + if (dwIRControl & rbIRVld) { + ucIRCount = (u8) smi_read(IR_Data_Cnt); + + if (ucIRCount < 4) + goto end_ir_decode; + + readLoop = ucIRCount/4; + if (ucIRCount % 4) + readLoop += 1; + for (index = 0; index < readLoop; index++) { + dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4)); + + ir->irData[index*4 + 0] = (u8)(dwIRData); + ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); + ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); + ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); + } + dwIRCode = smi_decode_rc5(ir->irData, ucIRCount); + + if (dwIRCode != 0xFFFFFFFF) { + rc5_command = dwIRCode & 0x3F; + rc5_system = (dwIRCode & 0x7C0) >> 6; + toggle = (dwIRCode & 0x800) ? 1 : 0; + scancode = rc5_system << 8 | rc5_command; + rc_keydown(rc_dev, RC_TYPE_RC5, scancode, toggle); + } + } +end_ir_decode: + smi_set(IR_Init_Reg, 0x04); + smi_ir_enableInterrupt(ir); +} + +/* ir functions call by main driver.*/ +int smi_ir_irq(struct smi_rc *ir, u32 int_status) +{ + int handled = 0; + + if (int_status & IR_X_INT) { + smi_ir_disableInterrupt(ir); + smi_ir_clearInterrupt(ir); + schedule_work(&ir->work); + handled = 1; + } + return handled; +} + +void smi_ir_start(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(IR_Idle_Cnt_Low, 0x00140070); + msleep(20); + smi_set(IR_Init_Reg, 0x90); + + smi_ir_enableInterrupt(ir); +} + +int smi_ir_init(struct smi_dev *dev) +{ + int ret; + struct rc_dev *rc_dev; + struct smi_rc *ir = &dev->ir; + + rc_dev = rc_allocate_device(); + if (!rc_dev) + return -ENOMEM; + + /* init input device */ + snprintf(ir->input_name, sizeof(ir->input_name), "IR (%s)", + dev->info->name); + snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", + pci_name(dev->pci_dev)); + + rc_dev->driver_name = "SMI_PCIe"; + rc_dev->input_phys = ir->input_phys; + rc_dev->input_name = ir->input_name; + rc_dev->input_id.bustype = BUS_PCI; + rc_dev->input_id.version = 1; + rc_dev->input_id.vendor = dev->pci_dev->subsystem_vendor; + rc_dev->input_id.product = dev->pci_dev->subsystem_device; + rc_dev->dev.parent = &dev->pci_dev->dev; + + rc_dev->driver_type = RC_DRIVER_SCANCODE; + rc_dev->map_name = RC_MAP_DVBSKY; + + ir->rc_dev = rc_dev; + ir->dev = dev; + + INIT_WORK(&ir->work, smi_ir_decode); + smi_ir_disableInterrupt(ir); + + ret = rc_register_device(rc_dev); + if (ret) + goto ir_err; + + return 0; +ir_err: + rc_free_device(rc_dev); + return ret; +} + +void smi_ir_exit(struct smi_dev *dev) +{ + struct smi_rc *ir = &dev->ir; + struct rc_dev *rc_dev = ir->rc_dev; + + smi_ir_stop(ir); + rc_unregister_device(rc_dev); + ir->rc_dev = NULL; +} diff --git a/kernel/drivers/media/pci/smipcie/smipcie-main.c b/kernel/drivers/media/pci/smipcie/smipcie-main.c new file mode 100644 index 000000000..b039a229b --- /dev/null +++ b/kernel/drivers/media/pci/smipcie/smipcie-main.c @@ -0,0 +1,1114 @@ +/* + * SMI PCIe driver for DVBSky cards. + * + * Copyright (C) 2014 Max nibble + * + * 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 "smipcie.h" +#include "m88ds3103.h" +#include "ts2020.h" +#include "m88rs6000t.h" +#include "si2168.h" +#include "si2157.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int smi_hw_init(struct smi_dev *dev) +{ + u32 port_mux, port_ctrl, int_stat; + + /* set port mux.*/ + port_mux = smi_read(MUX_MODE_CTRL); + port_mux &= ~(rbPaMSMask); + port_mux |= rbPaMSDtvNoGpio; + port_mux &= ~(rbPbMSMask); + port_mux |= rbPbMSDtvNoGpio; + port_mux &= ~(0x0f0000); + port_mux |= 0x50000; + smi_write(MUX_MODE_CTRL, port_mux); + + /* set DTV register.*/ + /* Port A */ + port_ctrl = smi_read(VIDEO_CTRL_STATUS_A); + port_ctrl &= ~0x01; + smi_write(VIDEO_CTRL_STATUS_A, port_ctrl); + port_ctrl = smi_read(MPEG2_CTRL_A); + port_ctrl &= ~0x40; + port_ctrl |= 0x80; + smi_write(MPEG2_CTRL_A, port_ctrl); + /* Port B */ + port_ctrl = smi_read(VIDEO_CTRL_STATUS_B); + port_ctrl &= ~0x01; + smi_write(VIDEO_CTRL_STATUS_B, port_ctrl); + port_ctrl = smi_read(MPEG2_CTRL_B); + port_ctrl &= ~0x40; + port_ctrl |= 0x80; + smi_write(MPEG2_CTRL_B, port_ctrl); + + /* disable and clear interrupt.*/ + smi_write(MSI_INT_ENA_CLR, ALL_INT); + int_stat = smi_read(MSI_INT_STATUS); + smi_write(MSI_INT_STATUS_CLR, int_stat); + + /* reset demod.*/ + smi_clear(PERIPHERAL_CTRL, 0x0303); + msleep(50); + smi_set(PERIPHERAL_CTRL, 0x0101); + return 0; +} + +/* i2c bit bus.*/ +static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl) +{ + u32 dwCtrl; + + dwCtrl = smi_read(sw_ctl); + dwCtrl &= ~0x18; /* disable output.*/ + dwCtrl |= 0x21; /* reset and software mode.*/ + dwCtrl &= ~0xff00; + dwCtrl |= 0x6400; + smi_write(sw_ctl, dwCtrl); + msleep(20); + dwCtrl = smi_read(sw_ctl); + dwCtrl &= ~0x20; + smi_write(sw_ctl, dwCtrl); +} + +static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl) +{ + if (state) { + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); + } else { + smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT); + /* set as output.*/ + smi_set(sw_ctl, SW_I2C_MSK_DAT_EN); + } +} + +static void smi_i2c_setscl(void *data, int state, u32 sw_ctl) +{ + struct smi_dev *dev = data; + + if (state) { + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); + } else { + smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT); + /* set as output.*/ + smi_set(sw_ctl, SW_I2C_MSK_CLK_EN); + } +} + +static int smi_i2c_getsda(void *data, u32 sw_ctl) +{ + struct smi_dev *dev = data; + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); + udelay(1); + return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0; +} + +static int smi_i2c_getscl(void *data, u32 sw_ctl) +{ + struct smi_dev *dev = data; + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); + udelay(1); + return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0; +} +/* i2c 0.*/ +static void smi_i2c0_setsda(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setsda(dev, state, I2C_A_SW_CTL); +} + +static void smi_i2c0_setscl(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setscl(dev, state, I2C_A_SW_CTL); +} + +static int smi_i2c0_getsda(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getsda(dev, I2C_A_SW_CTL); +} + +static int smi_i2c0_getscl(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getscl(dev, I2C_A_SW_CTL); +} +/* i2c 1.*/ +static void smi_i2c1_setsda(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setsda(dev, state, I2C_B_SW_CTL); +} + +static void smi_i2c1_setscl(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setscl(dev, state, I2C_B_SW_CTL); +} + +static int smi_i2c1_getsda(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getsda(dev, I2C_B_SW_CTL); +} + +static int smi_i2c1_getscl(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getscl(dev, I2C_B_SW_CTL); +} + +static int smi_i2c_init(struct smi_dev *dev) +{ + int ret; + + /* i2c bus 0 */ + smi_i2c_cfg(dev, I2C_A_SW_CTL); + i2c_set_adapdata(&dev->i2c_bus[0], dev); + strcpy(dev->i2c_bus[0].name, "SMI-I2C0"); + dev->i2c_bus[0].owner = THIS_MODULE; + dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev; + dev->i2c_bus[0].algo_data = &dev->i2c_bit[0]; + dev->i2c_bit[0].data = dev; + dev->i2c_bit[0].setsda = smi_i2c0_setsda; + dev->i2c_bit[0].setscl = smi_i2c0_setscl; + dev->i2c_bit[0].getsda = smi_i2c0_getsda; + dev->i2c_bit[0].getscl = smi_i2c0_getscl; + dev->i2c_bit[0].udelay = 12; + dev->i2c_bit[0].timeout = 10; + /* Raise SCL and SDA */ + smi_i2c0_setsda(dev, 1); + smi_i2c0_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bus[0]); + if (ret < 0) + return ret; + + /* i2c bus 1 */ + smi_i2c_cfg(dev, I2C_B_SW_CTL); + i2c_set_adapdata(&dev->i2c_bus[1], dev); + strcpy(dev->i2c_bus[1].name, "SMI-I2C1"); + dev->i2c_bus[1].owner = THIS_MODULE; + dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev; + dev->i2c_bus[1].algo_data = &dev->i2c_bit[1]; + dev->i2c_bit[1].data = dev; + dev->i2c_bit[1].setsda = smi_i2c1_setsda; + dev->i2c_bit[1].setscl = smi_i2c1_setscl; + dev->i2c_bit[1].getsda = smi_i2c1_getsda; + dev->i2c_bit[1].getscl = smi_i2c1_getscl; + dev->i2c_bit[1].udelay = 12; + dev->i2c_bit[1].timeout = 10; + /* Raise SCL and SDA */ + smi_i2c1_setsda(dev, 1); + smi_i2c1_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bus[1]); + if (ret < 0) + i2c_del_adapter(&dev->i2c_bus[0]); + + return ret; +} + +static void smi_i2c_exit(struct smi_dev *dev) +{ + i2c_del_adapter(&dev->i2c_bus[0]); + i2c_del_adapter(&dev->i2c_bus[1]); +} + +static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size) +{ + int ret; + u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff }; + + struct i2c_msg msg[] = { + { .addr = 0x50, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = 0x50, .flags = I2C_M_RD, + .buf = data, .len = size } + }; + + ret = i2c_transfer(i2c, msg, 2); + + if (ret != 2) { + dev_err(&i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + return ret; +} + +/* ts port interrupt operations */ +static void smi_port_disableInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_ENA_CLR, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +static void smi_port_enableInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_ENA_SET, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +static void smi_port_clearInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_STATUS_CLR, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +/* tasklet handler: DMA data to dmx.*/ +static void smi_dma_xfer(unsigned long data) +{ + struct smi_port *port = (struct smi_port *) data; + struct smi_dev *dev = port->dev; + u32 intr_status, finishedData, dmaManagement; + u8 dmaChan0State, dmaChan1State; + + intr_status = port->_int_status; + dmaManagement = smi_read(port->DMA_MANAGEMENT); + dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4); + dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20); + + /* CH-0 DMA interrupt.*/ + if ((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) { + dev_dbg(&dev->pci_dev->dev, + "Port[%d]-DMA CH0 engine complete successful !\n", + port->idx); + finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE); + finishedData &= 0x003FFFFF; + /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] + * indicate dma total transfer length and + * zero of [21:0] indicate dma total transfer length + * equal to 0x400000 (4MB)*/ + if (finishedData == 0) + finishedData = 0x00400000; + if (finishedData != SMI_TS_DMA_BUF_SIZE) { + dev_dbg(&dev->pci_dev->dev, + "DMA CH0 engine complete length mismatched, finish data=%d !\n", + finishedData); + } + dvb_dmx_swfilter_packets(&port->demux, + port->cpu_addr[0], (finishedData / 188)); + /*dvb_dmx_swfilter(&port->demux, + port->cpu_addr[0], finishedData);*/ + } + /* CH-1 DMA interrupt.*/ + if ((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) { + dev_dbg(&dev->pci_dev->dev, + "Port[%d]-DMA CH1 engine complete successful !\n", + port->idx); + finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE); + finishedData &= 0x003FFFFF; + /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] + * indicate dma total transfer length and + * zero of [21:0] indicate dma total transfer length + * equal to 0x400000 (4MB)*/ + if (finishedData == 0) + finishedData = 0x00400000; + if (finishedData != SMI_TS_DMA_BUF_SIZE) { + dev_dbg(&dev->pci_dev->dev, + "DMA CH1 engine complete length mismatched, finish data=%d !\n", + finishedData); + } + dvb_dmx_swfilter_packets(&port->demux, + port->cpu_addr[1], (finishedData / 188)); + /*dvb_dmx_swfilter(&port->demux, + port->cpu_addr[1], finishedData);*/ + } + /* restart DMA.*/ + if (intr_status & port->_dmaInterruptCH0) + dmaManagement |= 0x00000002; + if (intr_status & port->_dmaInterruptCH1) + dmaManagement |= 0x00020000; + smi_write(port->DMA_MANAGEMENT, dmaManagement); + /* Re-enable interrupts */ + smi_port_enableInterrupt(port); +} + +static void smi_port_dma_free(struct smi_port *port) +{ + if (port->cpu_addr[0]) { + pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, + port->cpu_addr[0], port->dma_addr[0]); + port->cpu_addr[0] = NULL; + } + if (port->cpu_addr[1]) { + pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, + port->cpu_addr[1], port->dma_addr[1]); + port->cpu_addr[1] = NULL; + } +} + +static int smi_port_init(struct smi_port *port, int dmaChanUsed) +{ + dev_dbg(&port->dev->pci_dev->dev, + "%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed); + port->enable = 0; + if (port->idx == 0) { + /* Port A */ + port->_dmaInterruptCH0 = dmaChanUsed & 0x01; + port->_dmaInterruptCH1 = dmaChanUsed & 0x02; + + port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW; + port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI; + port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE; + port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL; + port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW; + port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI; + port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE; + port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL; + port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT; + } else { + /* Port B */ + port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04; + port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08; + + port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW; + port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI; + port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE; + port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL; + port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW; + port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI; + port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE; + port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL; + port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT; + } + + if (port->_dmaInterruptCH0) { + port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[0]); + if (!port->cpu_addr[0]) { + dev_err(&port->dev->pci_dev->dev, + "Port[%d] DMA CH0 memory allocation failed!\n", + port->idx); + goto err; + } + } + + if (port->_dmaInterruptCH1) { + port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[1]); + if (!port->cpu_addr[1]) { + dev_err(&port->dev->pci_dev->dev, + "Port[%d] DMA CH1 memory allocation failed!\n", + port->idx); + goto err; + } + } + + smi_port_disableInterrupt(port); + tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port); + tasklet_disable(&port->tasklet); + port->enable = 1; + return 0; +err: + smi_port_dma_free(port); + return -ENOMEM; +} + +static void smi_port_exit(struct smi_port *port) +{ + smi_port_disableInterrupt(port); + tasklet_kill(&port->tasklet); + smi_port_dma_free(port); + port->enable = 0; +} + +static int smi_port_irq(struct smi_port *port, u32 int_status) +{ + u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1; + int handled = 0; + + if (int_status & port_req_irq) { + smi_port_disableInterrupt(port); + port->_int_status = int_status; + smi_port_clearInterrupt(port); + tasklet_schedule(&port->tasklet); + handled = 1; + } + return handled; +} + +static irqreturn_t smi_irq_handler(int irq, void *dev_id) +{ + struct smi_dev *dev = dev_id; + struct smi_port *port0 = &dev->ts_port[0]; + struct smi_port *port1 = &dev->ts_port[1]; + struct smi_rc *ir = &dev->ir; + int handled = 0; + + u32 intr_status = smi_read(MSI_INT_STATUS); + + /* ts0 interrupt.*/ + if (dev->info->ts_0) + handled += smi_port_irq(port0, intr_status); + + /* ts1 interrupt.*/ + if (dev->info->ts_1) + handled += smi_port_irq(port1, intr_status); + + /* ir interrupt.*/ + handled += smi_ir_irq(ir, intr_status); + + return IRQ_RETVAL(handled); +} + +static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter, + struct i2c_board_info *info) +{ + struct i2c_client *client; + + request_module(info->type); + client = i2c_new_device(adapter, info); + if (client == NULL || client->dev.driver == NULL) + goto err_add_i2c_client; + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + goto err_add_i2c_client; + } + return client; + +err_add_i2c_client: + client = NULL; + return client; +} + +static void smi_del_i2c_client(struct i2c_client *client) +{ + module_put(client->dev.driver->owner); + i2c_unregister_device(client); +} + +static const struct m88ds3103_config smi_dvbsky_m88ds3103_cfg = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, + .lnb_hv_pol = 0, + .lnb_en_pol = 1, +}; + +static int smi_dvbsky_m88ds3103_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + /* tuner I2C module */ + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *tuner_client; + struct i2c_board_info tuner_info; + struct ts2020_config ts2020_config = {}; + memset(&tuner_info, 0, sizeof(struct i2c_board_info)); + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + port->fe = dvb_attach(m88ds3103_attach, + &smi_dvbsky_m88ds3103_cfg, i2c, &tuner_i2c_adapter); + if (!port->fe) { + ret = -ENODEV; + return ret; + } + /* attach tuner */ + ts2020_config.fe = port->fe; + strlcpy(tuner_info.type, "ts2020", I2C_NAME_SIZE); + tuner_info.addr = 0x60; + tuner_info.platform_data = &ts2020_config; + tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); + if (!tuner_client) { + ret = -ENODEV; + goto err_tuner_i2c_device; + } + + /* delegate signal strength measurement to tuner */ + port->fe->ops.read_signal_strength = + port->fe->ops.tuner_ops.get_rf_strength; + + port->i2c_client_tuner = tuner_client; + return ret; + +err_tuner_i2c_device: + dvb_frontend_detach(port->fe); + return ret; +} + +static const struct m88ds3103_config smi_dvbsky_m88rs6000_cfg = { + .i2c_addr = 0x69, + .clock = 27000000, + .i2c_wr_max = 33, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, + .lnb_hv_pol = 0, + .lnb_en_pol = 1, +}; + +static int smi_dvbsky_m88rs6000_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + /* tuner I2C module */ + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *tuner_client; + struct i2c_board_info tuner_info; + struct m88rs6000t_config m88rs6000t_config; + + memset(&tuner_info, 0, sizeof(struct i2c_board_info)); + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + port->fe = dvb_attach(m88ds3103_attach, + &smi_dvbsky_m88rs6000_cfg, i2c, &tuner_i2c_adapter); + if (!port->fe) { + ret = -ENODEV; + return ret; + } + /* attach tuner */ + m88rs6000t_config.fe = port->fe; + strlcpy(tuner_info.type, "m88rs6000t", I2C_NAME_SIZE); + tuner_info.addr = 0x21; + tuner_info.platform_data = &m88rs6000t_config; + tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); + if (!tuner_client) { + ret = -ENODEV; + goto err_tuner_i2c_device; + } + + /* delegate signal strength measurement to tuner */ + port->fe->ops.read_signal_strength = + port->fe->ops.tuner_ops.get_rf_strength; + + port->i2c_client_tuner = tuner_client; + return ret; + +err_tuner_i2c_device: + dvb_frontend_detach(port->fe); + return ret; +} + +static int smi_dvbsky_sit2_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *client_tuner, *client_demod; + struct i2c_board_info client_info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* select i2c bus */ + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &tuner_i2c_adapter; + si2168_config.fe = &port->fe; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + + memset(&client_info, 0, sizeof(struct i2c_board_info)); + strlcpy(client_info.type, "si2168", I2C_NAME_SIZE); + client_info.addr = 0x64; + client_info.platform_data = &si2168_config; + + client_demod = smi_add_i2c_client(i2c, &client_info); + if (!client_demod) { + ret = -ENODEV; + return ret; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = port->fe; + si2157_config.if_port = 1; + + memset(&client_info, 0, sizeof(struct i2c_board_info)); + strlcpy(client_info.type, "si2157", I2C_NAME_SIZE); + client_info.addr = 0x60; + client_info.platform_data = &si2157_config; + + client_tuner = smi_add_i2c_client(tuner_i2c_adapter, &client_info); + if (!client_tuner) { + smi_del_i2c_client(port->i2c_client_demod); + port->i2c_client_demod = NULL; + ret = -ENODEV; + return ret; + } + port->i2c_client_tuner = client_tuner; + return ret; +} + +static int smi_fe_init(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct dvb_adapter *adap = &port->dvb_adapter; + u8 mac_ee[16]; + + dev_dbg(&port->dev->pci_dev->dev, + "%s: port %d, fe_type = %d\n", + __func__, port->idx, port->fe_type); + switch (port->fe_type) { + case DVBSKY_FE_M88DS3103: + ret = smi_dvbsky_m88ds3103_fe_attach(port); + break; + case DVBSKY_FE_M88RS6000: + ret = smi_dvbsky_m88rs6000_fe_attach(port); + break; + case DVBSKY_FE_SIT2: + ret = smi_dvbsky_sit2_fe_attach(port); + break; + } + if (ret < 0) + return ret; + + /* register dvb frontend */ + ret = dvb_register_frontend(adap, port->fe); + if (ret < 0) { + if (port->i2c_client_tuner) + smi_del_i2c_client(port->i2c_client_tuner); + if (port->i2c_client_demod) + smi_del_i2c_client(port->i2c_client_demod); + dvb_frontend_detach(port->fe); + return ret; + } + /* init MAC.*/ + ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16); + dev_info(&port->dev->pci_dev->dev, + "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8); + memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6); + return ret; +} + +static void smi_fe_exit(struct smi_port *port) +{ + dvb_unregister_frontend(port->fe); + /* remove I2C demod and tuner */ + if (port->i2c_client_tuner) + smi_del_i2c_client(port->i2c_client_tuner); + if (port->i2c_client_demod) + smi_del_i2c_client(port->i2c_client_demod); + dvb_frontend_detach(port->fe); +} + +static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = NULL; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} + +static u32 smi_config_DMA(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg; + u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1; + u32 dmaManagement = 0, tlpTransUnit = DMA_TRANS_UNIT_188; + u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0; + u64 mem; + + dmaManagement = smi_read(port->DMA_MANAGEMENT); + /* Setup Channel-0 */ + if (port->_dmaInterruptCH0) { + totalLength = SMI_TS_DMA_BUF_SIZE; + mem = port->dma_addr[0]; + dmaMemPtrLow = mem & 0xffffffff; + dmaMemPtrHi = mem >> 32; + dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) + | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); + dmaManagement |= dmaChanEnable | (dmaTransStart << 1) + | (chanLatencyTimer << 8); + /* write DMA register, start DMA engine */ + smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow); + smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi); + smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg); + } + /* Setup Channel-1 */ + if (port->_dmaInterruptCH1) { + totalLength = SMI_TS_DMA_BUF_SIZE; + mem = port->dma_addr[1]; + dmaMemPtrLow = mem & 0xffffffff; + dmaMemPtrHi = mem >> 32; + dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) + | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); + dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17) + | (chanLatencyTimer << 24); + /* write DMA register, start DMA engine */ + smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow); + smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi); + smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg); + } + return dmaManagement; +} + +static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct smi_port *port = dvbdmx->priv; + struct smi_dev *dev = port->dev; + u32 dmaManagement; + + if (port->users++ == 0) { + dmaManagement = smi_config_DMA(port); + smi_port_clearInterrupt(port); + smi_port_enableInterrupt(port); + smi_write(port->DMA_MANAGEMENT, dmaManagement); + tasklet_enable(&port->tasklet); + } + return port->users; +} + +static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct smi_port *port = dvbdmx->priv; + struct smi_dev *dev = port->dev; + + if (--port->users) + return port->users; + + tasklet_disable(&port->tasklet); + smi_port_disableInterrupt(port); + smi_clear(port->DMA_MANAGEMENT, 0x30003); + return 0; +} + +static int smi_dvb_init(struct smi_port *port) +{ + int ret; + struct dvb_adapter *adap = &port->dvb_adapter; + struct dvb_demux *dvbdemux = &port->demux; + + dev_dbg(&port->dev->pci_dev->dev, + "%s, port %d\n", __func__, port->idx); + + ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE, + &port->dev->pci_dev->dev, + adapter_nr); + if (ret < 0) { + dev_err(&port->dev->pci_dev->dev, "Fail to register DVB adapter.\n"); + return ret; + } + ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", + smi_start_feed, + smi_stop_feed, port); + if (ret < 0) + goto err_del_dvb_register_adapter; + + ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux, + &port->hw_frontend, + &port->mem_frontend, adap); + if (ret < 0) + goto err_del_dvb_dmx; + + ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux); + if (ret < 0) + goto err_del_dvb_dmxdev; + return 0; +err_del_dvb_dmxdev: + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); + dvb_dmxdev_release(&port->dmxdev); +err_del_dvb_dmx: + dvb_dmx_release(&port->demux); +err_del_dvb_register_adapter: + dvb_unregister_adapter(&port->dvb_adapter); + return ret; +} + +static void smi_dvb_exit(struct smi_port *port) +{ + struct dvb_demux *dvbdemux = &port->demux; + + dvb_net_release(&port->dvbnet); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); + dvb_dmxdev_release(&port->dmxdev); + dvb_dmx_release(&port->demux); + + dvb_unregister_adapter(&port->dvb_adapter); +} + +static int smi_port_attach(struct smi_dev *dev, + struct smi_port *port, int index) +{ + int ret, dmachs; + + port->dev = dev; + port->idx = index; + port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1; + dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1; + /* port init.*/ + ret = smi_port_init(port, dmachs); + if (ret < 0) + return ret; + /* dvb init.*/ + ret = smi_dvb_init(port); + if (ret < 0) + goto err_del_port_init; + /* fe init.*/ + ret = smi_fe_init(port); + if (ret < 0) + goto err_del_dvb_init; + return 0; +err_del_dvb_init: + smi_dvb_exit(port); +err_del_port_init: + smi_port_exit(port); + return ret; +} + +static void smi_port_detach(struct smi_port *port) +{ + smi_fe_exit(port); + smi_dvb_exit(port); + smi_port_exit(port); +} + +static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct smi_dev *dev; + int ret = -ENOMEM; + + if (pci_enable_device(pdev) < 0) + return -ENODEV; + + dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_pci_disable_device; + } + + dev->pci_dev = pdev; + pci_set_drvdata(pdev, dev); + dev->info = (struct smi_cfg_info *) id->driver_data; + dev_info(&dev->pci_dev->dev, + "card detected: %s\n", dev->info->name); + + dev->nr = dev->info->type; + dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0), + pci_resource_len(dev->pci_dev, 0)); + if (!dev->lmmio) { + ret = -ENOMEM; + goto err_kfree; + } + + /* should we set to 32bit DMA? */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_iounmap; + + pci_set_master(pdev); + + ret = smi_hw_init(dev); + if (ret < 0) + goto err_pci_iounmap; + + ret = smi_i2c_init(dev); + if (ret < 0) + goto err_pci_iounmap; + + if (dev->info->ts_0) { + ret = smi_port_attach(dev, &dev->ts_port[0], 0); + if (ret < 0) + goto err_del_i2c_adaptor; + } + + if (dev->info->ts_1) { + ret = smi_port_attach(dev, &dev->ts_port[1], 1); + if (ret < 0) + goto err_del_port0_attach; + } + + ret = smi_ir_init(dev); + if (ret < 0) + goto err_del_port1_attach; + +#ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ + if (pci_msi_enabled()) + ret = pci_enable_msi(dev->pci_dev); + if (ret) + dev_info(&dev->pci_dev->dev, "MSI not available.\n"); +#endif + + ret = request_irq(dev->pci_dev->irq, smi_irq_handler, + IRQF_SHARED, "SMI_PCIE", dev); + if (ret < 0) + goto err_del_ir; + + smi_ir_start(&dev->ir); + return 0; + +err_del_ir: + smi_ir_exit(dev); +err_del_port1_attach: + if (dev->info->ts_1) + smi_port_detach(&dev->ts_port[1]); +err_del_port0_attach: + if (dev->info->ts_0) + smi_port_detach(&dev->ts_port[0]); +err_del_i2c_adaptor: + smi_i2c_exit(dev); +err_pci_iounmap: + iounmap(dev->lmmio); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(dev); +err_pci_disable_device: + pci_disable_device(pdev); + return ret; +} + +static void smi_remove(struct pci_dev *pdev) +{ + struct smi_dev *dev = pci_get_drvdata(pdev); + + smi_write(MSI_INT_ENA_CLR, ALL_INT); + free_irq(dev->pci_dev->irq, dev); +#ifdef CONFIG_PCI_MSI + pci_disable_msi(dev->pci_dev); +#endif + if (dev->info->ts_1) + smi_port_detach(&dev->ts_port[1]); + if (dev->info->ts_0) + smi_port_detach(&dev->ts_port[0]); + + smi_ir_exit(dev); + smi_i2c_exit(dev); + iounmap(dev->lmmio); + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); + kfree(dev); +} + +/* DVBSky cards */ +static struct smi_cfg_info dvbsky_s950_cfg = { + .type = SMI_DVBSKY_S950, + .name = "DVBSky S950 V3", + .ts_0 = SMI_TS_NULL, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_NULL, + .fe_1 = DVBSKY_FE_M88DS3103, +}; + +static struct smi_cfg_info dvbsky_s952_cfg = { + .type = SMI_DVBSKY_S952, + .name = "DVBSky S952 V3", + .ts_0 = SMI_TS_DMA_BOTH, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_M88RS6000, + .fe_1 = DVBSKY_FE_M88RS6000, +}; + +static struct smi_cfg_info dvbsky_t9580_cfg = { + .type = SMI_DVBSKY_T9580, + .name = "DVBSky T9580 V3", + .ts_0 = SMI_TS_DMA_BOTH, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_SIT2, + .fe_1 = DVBSKY_FE_M88DS3103, +}; + +/* PCI IDs */ +#define SMI_ID(_subvend, _subdev, _driverdata) { \ + .vendor = SMI_VID, .device = SMI_PID, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long)&_driverdata } + +static const struct pci_device_id smi_id_table[] = { + SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg), + SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), + SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), + {0} +}; +MODULE_DEVICE_TABLE(pci, smi_id_table); + +static struct pci_driver smipcie_driver = { + .name = "SMI PCIe driver", + .id_table = smi_id_table, + .probe = smi_probe, + .remove = smi_remove, +}; + +module_pci_driver(smipcie_driver); + +MODULE_AUTHOR("Max nibble "); +MODULE_DESCRIPTION("SMI PCIe driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/pci/smipcie/smipcie.c b/kernel/drivers/media/pci/smipcie/smipcie.c deleted file mode 100644 index 411592524..000000000 --- a/kernel/drivers/media/pci/smipcie/smipcie.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * SMI PCIe driver for DVBSky cards. - * - * Copyright (C) 2014 Max nibble - * - * 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 "smipcie.h" -#include "m88ds3103.h" -#include "ts2020.h" -#include "m88rs6000t.h" -#include "si2168.h" -#include "si2157.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static int smi_hw_init(struct smi_dev *dev) -{ - u32 port_mux, port_ctrl, int_stat; - - /* set port mux.*/ - port_mux = smi_read(MUX_MODE_CTRL); - port_mux &= ~(rbPaMSMask); - port_mux |= rbPaMSDtvNoGpio; - port_mux &= ~(rbPbMSMask); - port_mux |= rbPbMSDtvNoGpio; - port_mux &= ~(0x0f0000); - port_mux |= 0x50000; - smi_write(MUX_MODE_CTRL, port_mux); - - /* set DTV register.*/ - /* Port A */ - port_ctrl = smi_read(VIDEO_CTRL_STATUS_A); - port_ctrl &= ~0x01; - smi_write(VIDEO_CTRL_STATUS_A, port_ctrl); - port_ctrl = smi_read(MPEG2_CTRL_A); - port_ctrl &= ~0x40; - port_ctrl |= 0x80; - smi_write(MPEG2_CTRL_A, port_ctrl); - /* Port B */ - port_ctrl = smi_read(VIDEO_CTRL_STATUS_B); - port_ctrl &= ~0x01; - smi_write(VIDEO_CTRL_STATUS_B, port_ctrl); - port_ctrl = smi_read(MPEG2_CTRL_B); - port_ctrl &= ~0x40; - port_ctrl |= 0x80; - smi_write(MPEG2_CTRL_B, port_ctrl); - - /* disable and clear interrupt.*/ - smi_write(MSI_INT_ENA_CLR, ALL_INT); - int_stat = smi_read(MSI_INT_STATUS); - smi_write(MSI_INT_STATUS_CLR, int_stat); - - /* reset demod.*/ - smi_clear(PERIPHERAL_CTRL, 0x0303); - msleep(50); - smi_set(PERIPHERAL_CTRL, 0x0101); - return 0; -} - -/* i2c bit bus.*/ -static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl) -{ - u32 dwCtrl; - - dwCtrl = smi_read(sw_ctl); - dwCtrl &= ~0x18; /* disable output.*/ - dwCtrl |= 0x21; /* reset and software mode.*/ - dwCtrl &= ~0xff00; - dwCtrl |= 0x6400; - smi_write(sw_ctl, dwCtrl); - msleep(20); - dwCtrl = smi_read(sw_ctl); - dwCtrl &= ~0x20; - smi_write(sw_ctl, dwCtrl); -} - -static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl) -{ - if (state) { - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); - } else { - smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT); - /* set as output.*/ - smi_set(sw_ctl, SW_I2C_MSK_DAT_EN); - } -} - -static void smi_i2c_setscl(void *data, int state, u32 sw_ctl) -{ - struct smi_dev *dev = data; - - if (state) { - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); - } else { - smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT); - /* set as output.*/ - smi_set(sw_ctl, SW_I2C_MSK_CLK_EN); - } -} - -static int smi_i2c_getsda(void *data, u32 sw_ctl) -{ - struct smi_dev *dev = data; - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); - udelay(1); - return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0; -} - -static int smi_i2c_getscl(void *data, u32 sw_ctl) -{ - struct smi_dev *dev = data; - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); - udelay(1); - return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0; -} -/* i2c 0.*/ -static void smi_i2c0_setsda(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setsda(dev, state, I2C_A_SW_CTL); -} - -static void smi_i2c0_setscl(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setscl(dev, state, I2C_A_SW_CTL); -} - -static int smi_i2c0_getsda(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getsda(dev, I2C_A_SW_CTL); -} - -static int smi_i2c0_getscl(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getscl(dev, I2C_A_SW_CTL); -} -/* i2c 1.*/ -static void smi_i2c1_setsda(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setsda(dev, state, I2C_B_SW_CTL); -} - -static void smi_i2c1_setscl(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setscl(dev, state, I2C_B_SW_CTL); -} - -static int smi_i2c1_getsda(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getsda(dev, I2C_B_SW_CTL); -} - -static int smi_i2c1_getscl(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getscl(dev, I2C_B_SW_CTL); -} - -static int smi_i2c_init(struct smi_dev *dev) -{ - int ret; - - /* i2c bus 0 */ - smi_i2c_cfg(dev, I2C_A_SW_CTL); - i2c_set_adapdata(&dev->i2c_bus[0], dev); - strcpy(dev->i2c_bus[0].name, "SMI-I2C0"); - dev->i2c_bus[0].owner = THIS_MODULE; - dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev; - dev->i2c_bus[0].algo_data = &dev->i2c_bit[0]; - dev->i2c_bit[0].data = dev; - dev->i2c_bit[0].setsda = smi_i2c0_setsda; - dev->i2c_bit[0].setscl = smi_i2c0_setscl; - dev->i2c_bit[0].getsda = smi_i2c0_getsda; - dev->i2c_bit[0].getscl = smi_i2c0_getscl; - dev->i2c_bit[0].udelay = 12; - dev->i2c_bit[0].timeout = 10; - /* Raise SCL and SDA */ - smi_i2c0_setsda(dev, 1); - smi_i2c0_setscl(dev, 1); - - ret = i2c_bit_add_bus(&dev->i2c_bus[0]); - if (ret < 0) - return ret; - - /* i2c bus 1 */ - smi_i2c_cfg(dev, I2C_B_SW_CTL); - i2c_set_adapdata(&dev->i2c_bus[1], dev); - strcpy(dev->i2c_bus[1].name, "SMI-I2C1"); - dev->i2c_bus[1].owner = THIS_MODULE; - dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev; - dev->i2c_bus[1].algo_data = &dev->i2c_bit[1]; - dev->i2c_bit[1].data = dev; - dev->i2c_bit[1].setsda = smi_i2c1_setsda; - dev->i2c_bit[1].setscl = smi_i2c1_setscl; - dev->i2c_bit[1].getsda = smi_i2c1_getsda; - dev->i2c_bit[1].getscl = smi_i2c1_getscl; - dev->i2c_bit[1].udelay = 12; - dev->i2c_bit[1].timeout = 10; - /* Raise SCL and SDA */ - smi_i2c1_setsda(dev, 1); - smi_i2c1_setscl(dev, 1); - - ret = i2c_bit_add_bus(&dev->i2c_bus[1]); - if (ret < 0) - i2c_del_adapter(&dev->i2c_bus[0]); - - return ret; -} - -static void smi_i2c_exit(struct smi_dev *dev) -{ - i2c_del_adapter(&dev->i2c_bus[0]); - i2c_del_adapter(&dev->i2c_bus[1]); -} - -static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size) -{ - int ret; - u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff }; - - struct i2c_msg msg[] = { - { .addr = 0x50, .flags = 0, - .buf = b0, .len = 2 }, - { .addr = 0x50, .flags = I2C_M_RD, - .buf = data, .len = size } - }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) { - dev_err(&i2c->dev, "%s: reg=0x%x (error=%d)\n", - __func__, reg, ret); - return ret; - } - return ret; -} - -/* ts port interrupt operations */ -static void smi_port_disableInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_ENA_CLR, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -static void smi_port_enableInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_ENA_SET, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -static void smi_port_clearInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_STATUS_CLR, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -/* tasklet handler: DMA data to dmx.*/ -static void smi_dma_xfer(unsigned long data) -{ - struct smi_port *port = (struct smi_port *) data; - struct smi_dev *dev = port->dev; - u32 intr_status, finishedData, dmaManagement; - u8 dmaChan0State, dmaChan1State; - - intr_status = port->_int_status; - dmaManagement = smi_read(port->DMA_MANAGEMENT); - dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4); - dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20); - - /* CH-0 DMA interrupt.*/ - if ((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) { - dev_dbg(&dev->pci_dev->dev, - "Port[%d]-DMA CH0 engine complete successful !\n", - port->idx); - finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE); - finishedData &= 0x003FFFFF; - /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] - * indicate dma total transfer length and - * zero of [21:0] indicate dma total transfer length - * equal to 0x400000 (4MB)*/ - if (finishedData == 0) - finishedData = 0x00400000; - if (finishedData != SMI_TS_DMA_BUF_SIZE) { - dev_dbg(&dev->pci_dev->dev, - "DMA CH0 engine complete length mismatched, finish data=%d !\n", - finishedData); - } - dvb_dmx_swfilter_packets(&port->demux, - port->cpu_addr[0], (finishedData / 188)); - /*dvb_dmx_swfilter(&port->demux, - port->cpu_addr[0], finishedData);*/ - } - /* CH-1 DMA interrupt.*/ - if ((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) { - dev_dbg(&dev->pci_dev->dev, - "Port[%d]-DMA CH1 engine complete successful !\n", - port->idx); - finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE); - finishedData &= 0x003FFFFF; - /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] - * indicate dma total transfer length and - * zero of [21:0] indicate dma total transfer length - * equal to 0x400000 (4MB)*/ - if (finishedData == 0) - finishedData = 0x00400000; - if (finishedData != SMI_TS_DMA_BUF_SIZE) { - dev_dbg(&dev->pci_dev->dev, - "DMA CH1 engine complete length mismatched, finish data=%d !\n", - finishedData); - } - dvb_dmx_swfilter_packets(&port->demux, - port->cpu_addr[1], (finishedData / 188)); - /*dvb_dmx_swfilter(&port->demux, - port->cpu_addr[1], finishedData);*/ - } - /* restart DMA.*/ - if (intr_status & port->_dmaInterruptCH0) - dmaManagement |= 0x00000002; - if (intr_status & port->_dmaInterruptCH1) - dmaManagement |= 0x00020000; - smi_write(port->DMA_MANAGEMENT, dmaManagement); - /* Re-enable interrupts */ - smi_port_enableInterrupt(port); -} - -static void smi_port_dma_free(struct smi_port *port) -{ - if (port->cpu_addr[0]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[0], port->dma_addr[0]); - port->cpu_addr[0] = NULL; - } - if (port->cpu_addr[1]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[1], port->dma_addr[1]); - port->cpu_addr[1] = NULL; - } -} - -static int smi_port_init(struct smi_port *port, int dmaChanUsed) -{ - dev_dbg(&port->dev->pci_dev->dev, - "%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed); - port->enable = 0; - if (port->idx == 0) { - /* Port A */ - port->_dmaInterruptCH0 = dmaChanUsed & 0x01; - port->_dmaInterruptCH1 = dmaChanUsed & 0x02; - - port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW; - port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI; - port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE; - port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL; - port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW; - port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI; - port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE; - port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL; - port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT; - } else { - /* Port B */ - port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04; - port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08; - - port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW; - port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI; - port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE; - port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL; - port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW; - port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI; - port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE; - port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL; - port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT; - } - - if (port->_dmaInterruptCH0) { - port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[0]); - if (!port->cpu_addr[0]) { - dev_err(&port->dev->pci_dev->dev, - "Port[%d] DMA CH0 memory allocation failed!\n", - port->idx); - goto err; - } - } - - if (port->_dmaInterruptCH1) { - port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[1]); - if (!port->cpu_addr[1]) { - dev_err(&port->dev->pci_dev->dev, - "Port[%d] DMA CH1 memory allocation failed!\n", - port->idx); - goto err; - } - } - - smi_port_disableInterrupt(port); - tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port); - tasklet_disable(&port->tasklet); - port->enable = 1; - return 0; -err: - smi_port_dma_free(port); - return -ENOMEM; -} - -static void smi_port_exit(struct smi_port *port) -{ - smi_port_disableInterrupt(port); - tasklet_kill(&port->tasklet); - smi_port_dma_free(port); - port->enable = 0; -} - -static int smi_port_irq(struct smi_port *port, u32 int_status) -{ - u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1; - int handled = 0; - - if (int_status & port_req_irq) { - smi_port_disableInterrupt(port); - port->_int_status = int_status; - smi_port_clearInterrupt(port); - tasklet_schedule(&port->tasklet); - handled = 1; - } - return handled; -} - -static irqreturn_t smi_irq_handler(int irq, void *dev_id) -{ - struct smi_dev *dev = dev_id; - struct smi_port *port0 = &dev->ts_port[0]; - struct smi_port *port1 = &dev->ts_port[1]; - int handled = 0; - - u32 intr_status = smi_read(MSI_INT_STATUS); - - /* ts0 interrupt.*/ - if (dev->info->ts_0) - handled += smi_port_irq(port0, intr_status); - - /* ts1 interrupt.*/ - if (dev->info->ts_1) - handled += smi_port_irq(port1, intr_status); - - return IRQ_RETVAL(handled); -} - -static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter, - struct i2c_board_info *info) -{ - struct i2c_client *client; - - request_module(info->type); - client = i2c_new_device(adapter, info); - if (client == NULL || client->dev.driver == NULL) - goto err_add_i2c_client; - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - goto err_add_i2c_client; - } - return client; - -err_add_i2c_client: - client = NULL; - return client; -} - -static void smi_del_i2c_client(struct i2c_client *client) -{ - module_put(client->dev.driver->owner); - i2c_unregister_device(client); -} - -static const struct m88ds3103_config smi_dvbsky_m88ds3103_cfg = { - .i2c_addr = 0x68, - .clock = 27000000, - .i2c_wr_max = 33, - .clock_out = 0, - .ts_mode = M88DS3103_TS_PARALLEL, - .ts_clk = 16000, - .ts_clk_pol = 1, - .agc = 0x99, - .lnb_hv_pol = 0, - .lnb_en_pol = 1, -}; - -static int smi_dvbsky_m88ds3103_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - /* tuner I2C module */ - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *tuner_client; - struct i2c_board_info tuner_info; - struct ts2020_config ts2020_config = {}; - memset(&tuner_info, 0, sizeof(struct i2c_board_info)); - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - port->fe = dvb_attach(m88ds3103_attach, - &smi_dvbsky_m88ds3103_cfg, i2c, &tuner_i2c_adapter); - if (!port->fe) { - ret = -ENODEV; - return ret; - } - /* attach tuner */ - ts2020_config.fe = port->fe; - strlcpy(tuner_info.type, "ts2020", I2C_NAME_SIZE); - tuner_info.addr = 0x60; - tuner_info.platform_data = &ts2020_config; - tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); - if (!tuner_client) { - ret = -ENODEV; - goto err_tuner_i2c_device; - } - - /* delegate signal strength measurement to tuner */ - port->fe->ops.read_signal_strength = - port->fe->ops.tuner_ops.get_rf_strength; - - port->i2c_client_tuner = tuner_client; - return ret; - -err_tuner_i2c_device: - dvb_frontend_detach(port->fe); - return ret; -} - -static const struct m88ds3103_config smi_dvbsky_m88rs6000_cfg = { - .i2c_addr = 0x69, - .clock = 27000000, - .i2c_wr_max = 33, - .ts_mode = M88DS3103_TS_PARALLEL, - .ts_clk = 16000, - .ts_clk_pol = 1, - .agc = 0x99, - .lnb_hv_pol = 0, - .lnb_en_pol = 1, -}; - -static int smi_dvbsky_m88rs6000_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - /* tuner I2C module */ - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *tuner_client; - struct i2c_board_info tuner_info; - struct m88rs6000t_config m88rs6000t_config; - - memset(&tuner_info, 0, sizeof(struct i2c_board_info)); - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - port->fe = dvb_attach(m88ds3103_attach, - &smi_dvbsky_m88rs6000_cfg, i2c, &tuner_i2c_adapter); - if (!port->fe) { - ret = -ENODEV; - return ret; - } - /* attach tuner */ - m88rs6000t_config.fe = port->fe; - strlcpy(tuner_info.type, "m88rs6000t", I2C_NAME_SIZE); - tuner_info.addr = 0x21; - tuner_info.platform_data = &m88rs6000t_config; - tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); - if (!tuner_client) { - ret = -ENODEV; - goto err_tuner_i2c_device; - } - - /* delegate signal strength measurement to tuner */ - port->fe->ops.read_signal_strength = - port->fe->ops.tuner_ops.get_rf_strength; - - port->i2c_client_tuner = tuner_client; - return ret; - -err_tuner_i2c_device: - dvb_frontend_detach(port->fe); - return ret; -} - -static int smi_dvbsky_sit2_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *client_tuner, *client_demod; - struct i2c_board_info client_info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* select i2c bus */ - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &tuner_i2c_adapter; - si2168_config.fe = &port->fe; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - - memset(&client_info, 0, sizeof(struct i2c_board_info)); - strlcpy(client_info.type, "si2168", I2C_NAME_SIZE); - client_info.addr = 0x64; - client_info.platform_data = &si2168_config; - - client_demod = smi_add_i2c_client(i2c, &client_info); - if (!client_demod) { - ret = -ENODEV; - return ret; - } - port->i2c_client_demod = client_demod; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = port->fe; - - memset(&client_info, 0, sizeof(struct i2c_board_info)); - strlcpy(client_info.type, "si2157", I2C_NAME_SIZE); - client_info.addr = 0x60; - client_info.platform_data = &si2157_config; - - client_tuner = smi_add_i2c_client(tuner_i2c_adapter, &client_info); - if (!client_tuner) { - smi_del_i2c_client(port->i2c_client_demod); - port->i2c_client_demod = NULL; - ret = -ENODEV; - return ret; - } - port->i2c_client_tuner = client_tuner; - return ret; -} - -static int smi_fe_init(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct dvb_adapter *adap = &port->dvb_adapter; - u8 mac_ee[16]; - - dev_dbg(&port->dev->pci_dev->dev, - "%s: port %d, fe_type = %d\n", - __func__, port->idx, port->fe_type); - switch (port->fe_type) { - case DVBSKY_FE_M88DS3103: - ret = smi_dvbsky_m88ds3103_fe_attach(port); - break; - case DVBSKY_FE_M88RS6000: - ret = smi_dvbsky_m88rs6000_fe_attach(port); - break; - case DVBSKY_FE_SIT2: - ret = smi_dvbsky_sit2_fe_attach(port); - break; - } - if (ret < 0) - return ret; - - /* register dvb frontend */ - ret = dvb_register_frontend(adap, port->fe); - if (ret < 0) { - if (port->i2c_client_tuner) - smi_del_i2c_client(port->i2c_client_tuner); - if (port->i2c_client_demod) - smi_del_i2c_client(port->i2c_client_demod); - dvb_frontend_detach(port->fe); - return ret; - } - /* init MAC.*/ - ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16); - dev_info(&port->dev->pci_dev->dev, - "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8); - memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6); - return ret; -} - -static void smi_fe_exit(struct smi_port *port) -{ - dvb_unregister_frontend(port->fe); - /* remove I2C demod and tuner */ - if (port->i2c_client_tuner) - smi_del_i2c_client(port->i2c_client_tuner); - if (port->i2c_client_demod) - smi_del_i2c_client(port->i2c_client_demod); - dvb_frontend_detach(port->fe); -} - -static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv) -{ - dvbdemux->priv = priv; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = start_feed; - dvbdemux->stop_feed = stop_feed; - dvbdemux->write_to_decoder = NULL; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - return dvb_dmx_init(dvbdemux); -} - -static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter) -{ - int ret; - - dmxdev->filternum = 256; - dmxdev->demux = &dvbdemux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adapter); - if (ret < 0) - return ret; - - hw_frontend->source = DMX_FRONTEND_0; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); - mem_frontend->source = DMX_MEMORY_FE; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); - return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); -} - -static u32 smi_config_DMA(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg; - u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1; - u32 dmaManagement = 0, tlpTransUnit = DMA_TRANS_UNIT_188; - u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0; - u64 mem; - - dmaManagement = smi_read(port->DMA_MANAGEMENT); - /* Setup Channel-0 */ - if (port->_dmaInterruptCH0) { - totalLength = SMI_TS_DMA_BUF_SIZE; - mem = port->dma_addr[0]; - dmaMemPtrLow = mem & 0xffffffff; - dmaMemPtrHi = mem >> 32; - dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) - | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); - dmaManagement |= dmaChanEnable | (dmaTransStart << 1) - | (chanLatencyTimer << 8); - /* write DMA register, start DMA engine */ - smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow); - smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi); - smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg); - } - /* Setup Channel-1 */ - if (port->_dmaInterruptCH1) { - totalLength = SMI_TS_DMA_BUF_SIZE; - mem = port->dma_addr[1]; - dmaMemPtrLow = mem & 0xffffffff; - dmaMemPtrHi = mem >> 32; - dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) - | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); - dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17) - | (chanLatencyTimer << 24); - /* write DMA register, start DMA engine */ - smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow); - smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi); - smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg); - } - return dmaManagement; -} - -static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct smi_port *port = dvbdmx->priv; - struct smi_dev *dev = port->dev; - u32 dmaManagement; - - if (port->users++ == 0) { - dmaManagement = smi_config_DMA(port); - smi_port_clearInterrupt(port); - smi_port_enableInterrupt(port); - smi_write(port->DMA_MANAGEMENT, dmaManagement); - tasklet_enable(&port->tasklet); - } - return port->users; -} - -static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct smi_port *port = dvbdmx->priv; - struct smi_dev *dev = port->dev; - - if (--port->users) - return port->users; - - tasklet_disable(&port->tasklet); - smi_port_disableInterrupt(port); - smi_clear(port->DMA_MANAGEMENT, 0x30003); - return 0; -} - -static int smi_dvb_init(struct smi_port *port) -{ - int ret; - struct dvb_adapter *adap = &port->dvb_adapter; - struct dvb_demux *dvbdemux = &port->demux; - - dev_dbg(&port->dev->pci_dev->dev, - "%s, port %d\n", __func__, port->idx); - - ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE, - &port->dev->pci_dev->dev, - adapter_nr); - if (ret < 0) { - dev_err(&port->dev->pci_dev->dev, "Fail to register DVB adapter.\n"); - return ret; - } - ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", - smi_start_feed, - smi_stop_feed, port); - if (ret < 0) - goto err_del_dvb_register_adapter; - - ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux, - &port->hw_frontend, - &port->mem_frontend, adap); - if (ret < 0) - goto err_del_dvb_dmx; - - ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux); - if (ret < 0) - goto err_del_dvb_dmxdev; - return 0; -err_del_dvb_dmxdev: - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); - dvb_dmxdev_release(&port->dmxdev); -err_del_dvb_dmx: - dvb_dmx_release(&port->demux); -err_del_dvb_register_adapter: - dvb_unregister_adapter(&port->dvb_adapter); - return ret; -} - -static void smi_dvb_exit(struct smi_port *port) -{ - struct dvb_demux *dvbdemux = &port->demux; - - dvb_net_release(&port->dvbnet); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); - dvb_dmxdev_release(&port->dmxdev); - dvb_dmx_release(&port->demux); - - dvb_unregister_adapter(&port->dvb_adapter); -} - -static int smi_port_attach(struct smi_dev *dev, - struct smi_port *port, int index) -{ - int ret, dmachs; - - port->dev = dev; - port->idx = index; - port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1; - dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1; - /* port init.*/ - ret = smi_port_init(port, dmachs); - if (ret < 0) - return ret; - /* dvb init.*/ - ret = smi_dvb_init(port); - if (ret < 0) - goto err_del_port_init; - /* fe init.*/ - ret = smi_fe_init(port); - if (ret < 0) - goto err_del_dvb_init; - return 0; -err_del_dvb_init: - smi_dvb_exit(port); -err_del_port_init: - smi_port_exit(port); - return ret; -} - -static void smi_port_detach(struct smi_port *port) -{ - smi_fe_exit(port); - smi_dvb_exit(port); - smi_port_exit(port); -} - -static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct smi_dev *dev; - int ret = -ENOMEM; - - if (pci_enable_device(pdev) < 0) - return -ENODEV; - - dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL); - if (!dev) { - ret = -ENOMEM; - goto err_pci_disable_device; - } - - dev->pci_dev = pdev; - pci_set_drvdata(pdev, dev); - dev->info = (struct smi_cfg_info *) id->driver_data; - dev_info(&dev->pci_dev->dev, - "card detected: %s\n", dev->info->name); - - dev->nr = dev->info->type; - dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0), - pci_resource_len(dev->pci_dev, 0)); - if (!dev->lmmio) { - ret = -ENOMEM; - goto err_kfree; - } - - /* should we set to 32bit DMA? */ - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret < 0) - goto err_pci_iounmap; - - pci_set_master(pdev); - - ret = smi_hw_init(dev); - if (ret < 0) - goto err_pci_iounmap; - - ret = smi_i2c_init(dev); - if (ret < 0) - goto err_pci_iounmap; - - if (dev->info->ts_0) { - ret = smi_port_attach(dev, &dev->ts_port[0], 0); - if (ret < 0) - goto err_del_i2c_adaptor; - } - - if (dev->info->ts_1) { - ret = smi_port_attach(dev, &dev->ts_port[1], 1); - if (ret < 0) - goto err_del_port0_attach; - } - -#ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ - if (pci_msi_enabled()) - ret = pci_enable_msi(dev->pci_dev); - if (ret) - dev_info(&dev->pci_dev->dev, "MSI not available.\n"); -#endif - - ret = request_irq(dev->pci_dev->irq, smi_irq_handler, - IRQF_SHARED, "SMI_PCIE", dev); - if (ret < 0) - goto err_del_port1_attach; - - return 0; - -err_del_port1_attach: - if (dev->info->ts_1) - smi_port_detach(&dev->ts_port[1]); -err_del_port0_attach: - if (dev->info->ts_0) - smi_port_detach(&dev->ts_port[0]); -err_del_i2c_adaptor: - smi_i2c_exit(dev); -err_pci_iounmap: - iounmap(dev->lmmio); -err_kfree: - pci_set_drvdata(pdev, NULL); - kfree(dev); -err_pci_disable_device: - pci_disable_device(pdev); - return ret; -} - -static void smi_remove(struct pci_dev *pdev) -{ - struct smi_dev *dev = pci_get_drvdata(pdev); - - smi_write(MSI_INT_ENA_CLR, ALL_INT); - free_irq(dev->pci_dev->irq, dev); -#ifdef CONFIG_PCI_MSI - pci_disable_msi(dev->pci_dev); -#endif - if (dev->info->ts_1) - smi_port_detach(&dev->ts_port[1]); - if (dev->info->ts_0) - smi_port_detach(&dev->ts_port[0]); - - smi_i2c_exit(dev); - iounmap(dev->lmmio); - pci_set_drvdata(pdev, NULL); - pci_disable_device(pdev); - kfree(dev); -} - -/* DVBSky cards */ -static struct smi_cfg_info dvbsky_s950_cfg = { - .type = SMI_DVBSKY_S950, - .name = "DVBSky S950 V3", - .ts_0 = SMI_TS_NULL, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_NULL, - .fe_1 = DVBSKY_FE_M88DS3103, -}; - -static struct smi_cfg_info dvbsky_s952_cfg = { - .type = SMI_DVBSKY_S952, - .name = "DVBSky S952 V3", - .ts_0 = SMI_TS_DMA_BOTH, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_M88RS6000, - .fe_1 = DVBSKY_FE_M88RS6000, -}; - -static struct smi_cfg_info dvbsky_t9580_cfg = { - .type = SMI_DVBSKY_T9580, - .name = "DVBSky T9580 V3", - .ts_0 = SMI_TS_DMA_BOTH, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_SIT2, - .fe_1 = DVBSKY_FE_M88DS3103, -}; - -/* PCI IDs */ -#define SMI_ID(_subvend, _subdev, _driverdata) { \ - .vendor = SMI_VID, .device = SMI_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ - .driver_data = (unsigned long)&_driverdata } - -static const struct pci_device_id smi_id_table[] = { - SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg), - SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), - SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), - {0} -}; -MODULE_DEVICE_TABLE(pci, smi_id_table); - -static struct pci_driver smipcie_driver = { - .name = "SMI PCIe driver", - .id_table = smi_id_table, - .probe = smi_probe, - .remove = smi_remove, -}; - -module_pci_driver(smipcie_driver); - -MODULE_AUTHOR("Max nibble "); -MODULE_DESCRIPTION("SMI PCIe driver"); -MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/pci/smipcie/smipcie.h b/kernel/drivers/media/pci/smipcie/smipcie.h index 10cdf20f4..68cdda28f 100644 --- a/kernel/drivers/media/pci/smipcie/smipcie.h +++ b/kernel/drivers/media/pci/smipcie/smipcie.h @@ -234,6 +234,17 @@ struct smi_cfg_info { int fe_1; }; +struct smi_rc { + struct smi_dev *dev; + struct rc_dev *rc_dev; + char input_phys[64]; + char input_name[64]; + struct work_struct work; + u8 irData[256]; + + int users; +}; + struct smi_port { struct smi_dev *dev; int idx; @@ -284,6 +295,9 @@ struct smi_dev { /* i2c */ struct i2c_adapter i2c_bus[2]; struct i2c_algo_bit_data i2c_bit[2]; + + /* ir */ + struct smi_rc ir; }; #define smi_read(reg) readl(dev->lmmio + ((reg)>>2)) @@ -296,4 +310,9 @@ struct smi_dev { #define smi_set(reg, bit) smi_andor((reg), (bit), (bit)) #define smi_clear(reg, bit) smi_andor((reg), (bit), 0) +int smi_ir_irq(struct smi_rc *ir, u32 int_status); +void smi_ir_start(struct smi_rc *ir); +void smi_ir_exit(struct smi_dev *dev); +int smi_ir_init(struct smi_dev *dev); + #endif /* #ifndef _SMI_PCIE_H_ */ diff --git a/kernel/drivers/media/pci/solo6x10/solo6x10-core.c b/kernel/drivers/media/pci/solo6x10/solo6x10-core.c index 570d119ea..f50d07229 100644 --- a/kernel/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/kernel/drivers/media/pci/solo6x10/solo6x10-core.c @@ -134,23 +134,11 @@ static irqreturn_t solo_isr(int irq, void *data) static void free_solo_dev(struct solo_dev *solo_dev) { - struct pci_dev *pdev; - - if (!solo_dev) - return; + struct pci_dev *pdev = solo_dev->pdev; if (solo_dev->dev.parent) device_unregister(&solo_dev->dev); - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - if (solo_dev->reg_base) { /* Bring down the sub-devices first */ solo_g723_exit(solo_dev); @@ -164,9 +152,8 @@ static void free_solo_dev(struct solo_dev *solo_dev) /* Now cleanup the PCI device */ solo_irq_off(solo_dev, ~0); + free_irq(pdev->irq, solo_dev); pci_iounmap(pdev, solo_dev->reg_base); - if (pdev->irq) - free_irq(pdev->irq, solo_dev); } pci_release_regions(pdev); @@ -483,7 +470,6 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) solo_dev->type = id->driver_data; solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); if (ret) goto fail_probe; diff --git a/kernel/drivers/media/pci/solo6x10/solo6x10-g723.c b/kernel/drivers/media/pci/solo6x10/solo6x10-g723.c index 7ddc76709..4a37a1c51 100644 --- a/kernel/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/kernel/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -48,10 +48,8 @@ /* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page * is broken down to 20 * 48 byte regions (one for each channel possible) * with the rest of the page being dummy data. */ -#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define PERIODS G723_FDMA_PAGES #define G723_INTR_ORDER 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << G723_INTR_ORDER) -#define PERIODS_MAX G723_FDMA_PAGES struct solo_snd_pcm { int on; @@ -130,11 +128,11 @@ static const struct snd_pcm_hardware snd_solo_pcm_hw = { .rate_max = SAMPLERATE, .channels_min = 1, .channels_max = 1, - .buffer_bytes_max = G723_MAX_BUFFER, + .buffer_bytes_max = G723_PERIOD_BYTES * PERIODS, .period_bytes_min = G723_PERIOD_BYTES, .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, + .periods_min = PERIODS, + .periods_max = PERIODS, }; static int snd_solo_pcm_open(struct snd_pcm_substream *ss) @@ -340,7 +338,8 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev) ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), - G723_MAX_BUFFER, G723_MAX_BUFFER); + G723_PERIOD_BYTES * PERIODS, + G723_PERIOD_BYTES * PERIODS); if (ret < 0) return ret; diff --git a/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 53fff5425..4432fd69b 100644 --- a/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -458,11 +458,12 @@ static inline u32 vop_usec(const vop_header *vh) static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, struct vb2_buffer *vb, const vop_header *vh) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct solo_dev *solo_dev = solo_enc->solo_dev; - struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); int frame_size; - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME; if (vb2_plane_size(vb, 0) < vop_jpeg_size(vh) + solo_enc->jpeg_len) return -EIO; @@ -470,7 +471,7 @@ static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, frame_size = ALIGN(vop_jpeg_size(vh) + solo_enc->jpeg_len, DMA_ALIGN); vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len); - return solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, + return solo_send_desc(solo_enc, solo_enc->jpeg_len, sgt, vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev), frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), SOLO_JPEG_EXT_SIZE(solo_dev)); @@ -479,8 +480,9 @@ static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, struct vb2_buffer *vb, const vop_header *vh) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct solo_dev *solo_dev = solo_enc->solo_dev; - struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); int frame_off, frame_size; int skip = 0; @@ -488,15 +490,15 @@ static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, return -EIO; /* If this is a key frame, add extra header */ - vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | + vbuf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME); if (!vop_type(vh)) { skip = solo_enc->vop_len; - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME; vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh) + solo_enc->vop_len); } else { - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + vbuf->flags |= V4L2_BUF_FLAG_PFRAME; vb2_set_plane_payload(vb, 0, vop_mpeg_size(vh)); } @@ -505,7 +507,7 @@ static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, sizeof(*vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); frame_size = ALIGN(vop_mpeg_size(vh) + skip, DMA_ALIGN); - return solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, + return solo_send_desc(solo_enc, skip, sgt, frame_off, frame_size, SOLO_MP4E_EXT_ADDR(solo_dev), SOLO_MP4E_EXT_SIZE(solo_dev)); } @@ -513,6 +515,7 @@ static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); const vop_header *vh = enc_buf->vh; int ret; @@ -527,17 +530,18 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, } if (!ret) { - vb->v4l2_buf.sequence = solo_enc->sequence++; - vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); - vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); + vbuf->sequence = solo_enc->sequence++; + vbuf->timestamp.tv_sec = vop_sec(vh); + vbuf->timestamp.tv_usec = vop_usec(vh); /* Check for motion flags */ if (solo_is_motion_on(solo_enc) && enc_buf->motion) { struct v4l2_event ev = { .type = V4L2_EVENT_MOTION_DET, .u.motion_det = { - .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, - .frame_sequence = vb->v4l2_buf.sequence, + .flags + = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = vbuf->sequence, .region_mask = enc_buf->motion ? 1 : 0, }, }; @@ -571,7 +575,7 @@ static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, list_del(&vb->list); spin_unlock_irqrestore(&solo_enc->av_lock, flags); - solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); + solo_enc_fillbuf(solo_enc, &vb->vb.vb2_buf, enc_buf); unlock: mutex_unlock(&solo_enc->lock); } @@ -659,7 +663,7 @@ static int solo_ring_thread(void *data) } static int solo_enc_queue_setup(struct vb2_queue *q, - const struct v4l2_format *fmt, + const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) @@ -678,10 +682,11 @@ static int solo_enc_queue_setup(struct vb2_queue *q, static void solo_enc_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); + container_of(vbuf, struct solo_vb2_buf, vb); spin_lock(&solo_enc->av_lock); list_add_tail(&solo_vb->list, &solo_enc->vidq_active); @@ -734,25 +739,26 @@ static void solo_enc_stop_streaming(struct vb2_queue *q) struct solo_vb2_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&solo_enc->av_lock, flags); } static void solo_enc_buf_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vb->vb2_queue); - struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); switch (solo_enc->fmt) { case V4L2_PIX_FMT_MPEG4: case V4L2_PIX_FMT_H264: - if (vb->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) - sg_copy_from_buffer(vbuf->sgl, vbuf->nents, + if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME) + sg_copy_from_buffer(sgt->sgl, sgt->nents, solo_enc->vop, solo_enc->vop_len); break; default: /* V4L2_PIX_FMT_MJPEG */ - sg_copy_from_buffer(vbuf->sgl, vbuf->nents, + sg_copy_from_buffer(sgt->sgl, sgt->nents, solo_enc->jpeg_header, solo_enc->jpeg_len); break; } @@ -1291,7 +1297,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->vidq.ops = &solo_enc_video_qops; solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; solo_enc->vidq.drv_priv = solo_enc; - solo_enc->vidq.gfp_flags = __GFP_DMA32; + solo_enc->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM; solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); solo_enc->vidq.lock = &solo_enc->lock; diff --git a/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 63ae8a61f..f7ce493b1 100644 --- a/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/kernel/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "solo6x10.h" @@ -191,13 +192,14 @@ static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) static void solo_fillbuf(struct solo_dev *solo_dev, struct vb2_buffer *vb) { - dma_addr_t vbuf; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + dma_addr_t addr; unsigned int fdma_addr; int error = -1; int i; - vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); - if (!vbuf) + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!addr) goto finish_buf; if (erase_off(solo_dev)) { @@ -213,7 +215,7 @@ static void solo_fillbuf(struct solo_dev *solo_dev, fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * (SOLO_HW_BPL * solo_vlines(solo_dev))); - error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, + error = solo_p2m_dma_t(solo_dev, 0, addr, fdma_addr, solo_bytesperline(solo_dev), solo_vlines(solo_dev), SOLO_HW_BPL); } @@ -222,8 +224,8 @@ finish_buf: if (!error) { vb2_set_plane_payload(vb, 0, solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); - vb->v4l2_buf.sequence = solo_dev->sequence++; - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + vbuf->sequence = solo_dev->sequence++; + v4l2_get_timestamp(&vbuf->timestamp); } vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); @@ -256,7 +258,7 @@ static void solo_thread_try(struct solo_dev *solo_dev) spin_unlock(&solo_dev->slock); - solo_fillbuf(solo_dev, &vb->vb); + solo_fillbuf(solo_dev, &vb->vb.vb2_buf); } assert_spin_locked(&solo_dev->slock); @@ -311,7 +313,7 @@ static void solo_stop_thread(struct solo_dev *solo_dev) solo_dev->kthread = NULL; } -static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int solo_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -345,10 +347,11 @@ static void solo_stop_streaming(struct vb2_queue *q) static void solo_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct solo_dev *solo_dev = vb2_get_drv_priv(vq); struct solo_vb2_buf *solo_vb = - container_of(vb, struct solo_vb2_buf, vb); + container_of(vbuf, struct solo_vb2_buf, vb); spin_lock(&solo_dev->slock); list_add_tail(&solo_vb->list, &solo_dev->vidq_active); @@ -675,7 +678,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; solo_dev->vidq.drv_priv = solo_dev; solo_dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - solo_dev->vidq.gfp_flags = __GFP_DMA32; + solo_dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM; solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); solo_dev->vidq.lock = &solo_dev->lock; ret = vb2_queue_init(&solo_dev->vidq); diff --git a/kernel/drivers/media/pci/solo6x10/solo6x10.h b/kernel/drivers/media/pci/solo6x10/solo6x10.h index 1ca54b08b..4ab6586c0 100644 --- a/kernel/drivers/media/pci/solo6x10/solo6x10.h +++ b/kernel/drivers/media/pci/solo6x10/solo6x10.h @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "solo6x10-regs.h" @@ -135,7 +135,7 @@ struct solo_p2m_dev { #define OSD_TEXT_MAX 44 struct solo_vb2_buf { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -199,7 +199,6 @@ struct solo_dev { int nr_ext; u32 irq_mask; u32 motion_mask; - spinlock_t reg_io_lock; struct v4l2_device v4l2_dev; /* tw28xx accounting */ @@ -281,36 +280,13 @@ struct solo_dev { static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) { - unsigned long flags; - u32 ret; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - ret = readl(solo_dev->reg_base + reg); - rmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); - - return ret; + return readl(solo_dev->reg_base + reg); } static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) { - unsigned long flags; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - writel(data, solo_dev->reg_base + reg); - wmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); } static inline void solo_irq_on(struct solo_dev *dev, u32 mask) diff --git a/kernel/drivers/media/pci/sta2x11/sta2x11_vip.c b/kernel/drivers/media/pci/sta2x11/sta2x11_vip.c index d384a6b0b..6367b455a 100644 --- a/kernel/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/kernel/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -88,11 +88,11 @@ struct vip_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; dma_addr_t dma; }; -static inline struct vip_buffer *to_vip_buffer(struct vb2_buffer *vb2) +static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) { return container_of(vb2, struct vip_buffer, vb); } @@ -265,7 +265,7 @@ static void vip_active_buf_next(struct sta2x11_vip *vip) /* Videobuf2 Operations */ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -287,7 +287,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, }; static int buffer_init(struct vb2_buffer *vb) { - struct vip_buffer *vip_buf = to_vip_buffer(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vip_buffer *vip_buf = to_vip_buffer(vbuf); vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0); INIT_LIST_HEAD(&vip_buf->list); @@ -296,8 +297,9 @@ static int buffer_init(struct vb2_buffer *vb) static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vb); + struct vip_buffer *vip_buf = to_vip_buffer(vbuf); unsigned long size; size = vip->format.sizeimage; @@ -307,14 +309,15 @@ static int buffer_prepare(struct vb2_buffer *vb) return -EINVAL; } - vb2_set_plane_payload(&vip_buf->vb, 0, size); + vb2_set_plane_payload(&vip_buf->vb.vb2_buf, 0, size); return 0; } static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vb); + struct vip_buffer *vip_buf = to_vip_buffer(vbuf); spin_lock(&vip->lock); list_add_tail(&vip_buf->list, &vip->buffer_list); @@ -329,8 +332,9 @@ static void buffer_queue(struct vb2_buffer *vb) } static void buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vb); + struct vip_buffer *vip_buf = to_vip_buffer(vbuf); /* Buffer handled, remove it from the list */ spin_lock(&vip->lock); @@ -370,7 +374,7 @@ static void stop_streaming(struct vb2_queue *vq) /* Release all active buffers */ spin_lock(&vip->lock); list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) { - vb2_buffer_done(&vip_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&vip_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); list_del(&vip_buf->list); } spin_unlock(&vip->lock); @@ -813,9 +817,9 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) /* Disable acquisition */ reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); /* Remove the active buffer from the list */ - do_gettimeofday(&vip->active->vb.v4l2_buf.timestamp); - vip->active->vb.v4l2_buf.sequence = vip->sequence++; - vb2_buffer_done(&vip->active->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vip->active->vb.timestamp); + vip->active->vb.sequence = vip->sequence++; + vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE); } return IRQ_HANDLED; @@ -864,6 +868,7 @@ static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer); vip->vb_vidq.ops = &vip_video_qops; vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; + vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; err = vb2_queue_init(&vip->vb_vidq); if (err) return err; diff --git a/kernel/drivers/media/pci/ttpci/av7110.c b/kernel/drivers/media/pci/ttpci/av7110.c index 45199a12b..f89364951 100644 --- a/kernel/drivers/media/pci/ttpci/av7110.c +++ b/kernel/drivers/media/pci/ttpci/av7110.c @@ -303,7 +303,6 @@ static int arm_thread(void *data) static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, u8 *buffer2, size_t buffer2_len, struct dvb_demux_filter *dvbdmxfilter, - enum dmx_success success, struct av7110 *av7110) { if (!dvbdmxfilter->feed->demux->dmx.frontend) @@ -329,16 +328,14 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, buffer2, buffer2_len, - &dvbdmxfilter->filter, - DMX_OK); + &dvbdmxfilter->filter); case DMX_TYPE_TS: if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) return 0; if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, buffer2, buffer2_len, - &dvbdmxfilter->feed->feed.ts, - DMX_OK); + &dvbdmxfilter->feed->feed.ts); else av7110_p2t_write(buffer1, buffer1_len, dvbdmxfilter->feed->pid, @@ -422,7 +419,7 @@ static void debiirq(unsigned long cookie) DvbDmxFilterCallback((u8 *)av7110->debi_virt, av7110->debilen, NULL, 0, av7110->handle2filter[handle], - DMX_OK, av7110); + av7110); xfer = RX_BUFF; break; @@ -1172,7 +1169,7 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, ******************************************************************************/ -static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct av7110* av7110 = fe->dvb->priv; @@ -1197,7 +1194,7 @@ static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, } static int av7110_diseqc_send_burst(struct dvb_frontend* fe, - fe_sec_mini_cmd_t minicmd) + enum fe_sec_mini_cmd minicmd) { struct av7110* av7110 = fe->dvb->priv; @@ -1946,7 +1943,7 @@ static struct l64781_config grundig_29504_401_config = { -static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) +static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) { int ret = 0; int synced = (status & FE_HAS_LOCK) ? 1 : 0; @@ -2008,7 +2005,8 @@ static int av7110_fe_init(struct dvb_frontend* fe) return ret; } -static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +static int av7110_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct av7110* av7110 = fe->dvb->priv; @@ -2043,7 +2041,8 @@ static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, return ret; } -static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct av7110* av7110 = fe->dvb->priv; @@ -2055,7 +2054,8 @@ static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_ return ret; } -static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int av7110_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct av7110* av7110 = fe->dvb->priv; @@ -2067,7 +2067,8 @@ static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return ret; } -static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int av7110_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct av7110* av7110 = fe->dvb->priv; diff --git a/kernel/drivers/media/pci/ttpci/av7110.h b/kernel/drivers/media/pci/ttpci/av7110.h index 835635b0c..3a55927ed 100644 --- a/kernel/drivers/media/pci/ttpci/av7110.h +++ b/kernel/drivers/media/pci/ttpci/av7110.h @@ -269,25 +269,30 @@ struct av7110 { unsigned long size_root; struct dvb_frontend* fe; - fe_status_t fe_status; + enum fe_status fe_status; struct mutex ioctl_mutex; /* crash recovery */ void (*recover)(struct av7110* av7110); - fe_sec_voltage_t saved_voltage; - fe_sec_tone_mode_t saved_tone; + enum fe_sec_voltage saved_voltage; + enum fe_sec_tone_mode saved_tone; struct dvb_diseqc_master_cmd saved_master_cmd; - fe_sec_mini_cmd_t saved_minicmd; + enum fe_sec_mini_cmd saved_minicmd; int (*fe_init)(struct dvb_frontend* fe); - int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); - int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); - int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); - int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); - int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); - int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); - int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); + int (*fe_read_status)(struct dvb_frontend *fe, enum fe_status *status); + int (*fe_diseqc_reset_overload)(struct dvb_frontend *fe); + int (*fe_diseqc_send_master_cmd)(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd); + int (*fe_diseqc_send_burst)(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd); + int (*fe_set_tone)(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone); + int (*fe_set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); + int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend *fe, + unsigned long cmd); int (*fe_set_frontend)(struct dvb_frontend *fe); }; diff --git a/kernel/drivers/media/pci/ttpci/av7110_av.c b/kernel/drivers/media/pci/ttpci/av7110_av.c index 9544cfc06..9ed1ec7d3 100644 --- a/kernel/drivers/media/pci/ttpci/av7110_av.c +++ b/kernel/drivers/media/pci/ttpci/av7110_av.c @@ -102,7 +102,7 @@ int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) buf[4] = buf[5] = 0; if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfeed->cb.ts(buf, len, NULL, 0, - &dvbdmxfeed->feed.ts, DMX_OK); + &dvbdmxfeed->feed.ts); else return dvb_filter_pes2ts(p2t, buf, len, 1); } @@ -112,7 +112,7 @@ static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; dvbdmxfeed->cb.ts(data, 188, NULL, 0, - &dvbdmxfeed->feed.ts, DMX_OK); + &dvbdmxfeed->feed.ts); return 0; } @@ -815,7 +815,7 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, memcpy(obuf + l, buf + c, TS_SIZE - l); c = length; } - feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK); + feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts); pes_start = 0; } } diff --git a/kernel/drivers/media/pci/ttpci/budget-av.c b/kernel/drivers/media/pci/ttpci/budget-av.c index 54c991025..3e469d4e0 100644 --- a/kernel/drivers/media/pci/ttpci/budget-av.c +++ b/kernel/drivers/media/pci/ttpci/budget-av.c @@ -1508,7 +1508,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", budget_av->budget.dvb_adapter.num); - memset(mac, 0, 6); + eth_zero_addr(mac); } else { pr_info("KNC1-%d: MAC addr = %pM\n", budget_av->budget.dvb_adapter.num, mac); diff --git a/kernel/drivers/media/pci/ttpci/budget-core.c b/kernel/drivers/media/pci/ttpci/budget-core.c index 23e05499b..e9674b400 100644 --- a/kernel/drivers/media/pci/ttpci/budget-core.c +++ b/kernel/drivers/media/pci/ttpci/budget-core.c @@ -161,7 +161,8 @@ static int start_ts_capture(struct budget *budget) return 0; } -static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status) +static int budget_read_fe_status(struct dvb_frontend *fe, + enum fe_status *status) { struct budget *budget = (struct budget *) fe->dvb->priv; int synced; diff --git a/kernel/drivers/media/pci/ttpci/budget-patch.c b/kernel/drivers/media/pci/ttpci/budget-patch.c index a4d8867e1..b5b65962c 100644 --- a/kernel/drivers/media/pci/ttpci/budget-patch.c +++ b/kernel/drivers/media/pci/ttpci/budget-patch.c @@ -128,9 +128,9 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long return 0; } -/* shamelessly copy/pasted from budget.c -*/ -static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +/* shamelessly copy/pasted from budget.c */ +static int budget_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct budget* budget = (struct budget*) fe->dvb->priv; @@ -159,7 +159,8 @@ static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_dis return 0; } -static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int budget_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct budget* budget = (struct budget*) fe->dvb->priv; @@ -223,7 +224,8 @@ static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, return 0; } -static int budget_patch_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int budget_patch_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; @@ -252,7 +254,8 @@ static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct d return 0; } -static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int budget_patch_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; diff --git a/kernel/drivers/media/pci/ttpci/budget.c b/kernel/drivers/media/pci/ttpci/budget.c index 6ccc48833..99972beca 100644 --- a/kernel/drivers/media/pci/ttpci/budget.c +++ b/kernel/drivers/media/pci/ttpci/budget.c @@ -132,7 +132,8 @@ static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long * Voltage must be set here. * GPIO 1: LNBP EN, GPIO 2: LNBP VSEL */ -static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage) +static int SetVoltage_Activy(struct budget *budget, + enum fe_sec_voltage voltage) { struct saa7146_dev *dev=budget->dev; @@ -157,14 +158,16 @@ static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage) return 0; } -static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int siemens_budget_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct budget* budget = (struct budget*) fe->dvb->priv; return SetVoltage_Activy (budget, voltage); } -static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int budget_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct budget* budget = (struct budget*) fe->dvb->priv; @@ -193,7 +196,8 @@ static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_dis return 0; } -static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +static int budget_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) { struct budget* budget = (struct budget*) fe->dvb->priv; diff --git a/kernel/drivers/media/pci/ttpci/budget.h b/kernel/drivers/media/pci/ttpci/budget.h index 3d8a806c2..1ccbe1a49 100644 --- a/kernel/drivers/media/pci/ttpci/budget.h +++ b/kernel/drivers/media/pci/ttpci/budget.h @@ -72,7 +72,7 @@ struct budget { struct dvb_adapter dvb_adapter; struct dvb_frontend *dvb_frontend; - int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status); + int (*read_fe_status)(struct dvb_frontend *fe, enum fe_status *status); int fe_synced; void *priv; diff --git a/kernel/drivers/media/pci/ttpci/ttpci-eeprom.c b/kernel/drivers/media/pci/ttpci/ttpci-eeprom.c index 32d43156c..079ee098b 100644 --- a/kernel/drivers/media/pci/ttpci/ttpci-eeprom.c +++ b/kernel/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "ttpci-eeprom.h" @@ -145,7 +146,7 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) if (ret != 0) { /* Will only be -ENODEV */ dprintk("Couldn't read from EEPROM: not there?\n"); - memset(proposed_mac, 0, 6); + eth_zero_addr(proposed_mac); return ret; } @@ -157,14 +158,12 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) dprintk( "%.2x:", encodedMAC[i]); } dprintk("%.2x\n", encodedMAC[19]); - memset(proposed_mac, 0, 6); + eth_zero_addr(proposed_mac); return ret; } memcpy(proposed_mac, decodedMAC, 6); - dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", - decodedMAC[0], decodedMAC[1], decodedMAC[2], - decodedMAC[3], decodedMAC[4], decodedMAC[5]); + dprintk("adapter has MAC addr = %pM\n", decodedMAC); return 0; } diff --git a/kernel/drivers/media/pci/tw68/tw68-core.c b/kernel/drivers/media/pci/tw68/tw68-core.c index c135165a8..4e77618fb 100644 --- a/kernel/drivers/media/pci/tw68/tw68-core.c +++ b/kernel/drivers/media/pci/tw68/tw68-core.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -70,13 +71,13 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ static const struct pci_device_id tw68_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6800)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6801)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6804)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_1)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_3)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_4)}, {0,} }; @@ -256,22 +257,22 @@ static int tw68_initdev(struct pci_dev *pci_dev, dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat, (u64)pci_resource_start(pci_dev, 0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name); - err = -EIO; goto fail1; } switch (pci_id->device) { - case PCI_DEVICE_ID_6800: /* TW6800 */ + case PCI_DEVICE_ID_TECHWELL_6800: /* TW6800 */ dev->vdecoder = TW6800; dev->board_virqmask = TW68_VID_INTS; break; - case PCI_DEVICE_ID_6801: /* Video decoder for TW6802 */ + case PCI_DEVICE_ID_TECHWELL_6801: /* Video decoder for TW6802 */ dev->vdecoder = TW6801; dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; break; - case PCI_DEVICE_ID_6804: /* Video decoder for TW6804 */ + case PCI_DEVICE_ID_TECHWELL_6804: /* Video decoder for TW6804 */ dev->vdecoder = TW6804; dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; break; diff --git a/kernel/drivers/media/pci/tw68/tw68-video.c b/kernel/drivers/media/pci/tw68/tw68-video.c index 8355e55b4..46642ef91 100644 --- a/kernel/drivers/media/pci/tw68/tw68-video.c +++ b/kernel/drivers/media/pci/tw68/tw68-video.c @@ -376,10 +376,11 @@ static int tw68_buffer_count(unsigned int size, unsigned int count) /* ------------------------------------------------------------- */ /* vb2 queue operations */ -static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int tw68_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct tw68_dev *dev = vb2_get_drv_priv(q); unsigned tot_bufs = q->num_buffers + *num_buffers; @@ -423,9 +424,10 @@ static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, */ static void tw68_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct tw68_dev *dev = vb2_get_drv_priv(vq); - struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb); struct tw68_buf *prev; unsigned long flags; @@ -457,9 +459,10 @@ static void tw68_buf_queue(struct vb2_buffer *vb) */ static int tw68_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct tw68_dev *dev = vb2_get_drv_priv(vq); - struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb); struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0); unsigned size, bpl; @@ -499,9 +502,10 @@ static int tw68_buf_prepare(struct vb2_buffer *vb) static void tw68_buf_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct tw68_dev *dev = vb2_get_drv_priv(vq); - struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb); pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma); } @@ -528,7 +532,7 @@ static void tw68_stop_streaming(struct vb2_queue *q) container_of(dev->active.next, struct tw68_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } } @@ -975,7 +979,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr) dev->vidq.ops = &tw68_video_qops; dev->vidq.mem_ops = &vb2_dma_sg_memops; dev->vidq.drv_priv = dev; - dev->vidq.gfp_flags = __GFP_DMA32; + dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM; dev->vidq.buf_struct_size = sizeof(struct tw68_buf); dev->vidq.lock = &dev->lock; dev->vidq.min_buffers_needed = 2; @@ -1012,10 +1016,10 @@ void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status) buf = list_entry(dev->active.next, struct tw68_buf, list); list_del(&buf->list); spin_unlock(&dev->slock); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.field = dev->field; - buf->vb.v4l2_buf.sequence = dev->seqnr++; - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.field = dev->field; + buf->vb.sequence = dev->seqnr++; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); status &= ~(TW68_DMAPI); if (0 == status) return; diff --git a/kernel/drivers/media/pci/tw68/tw68.h b/kernel/drivers/media/pci/tw68/tw68.h index 93f2335e0..6c7dcb300 100644 --- a/kernel/drivers/media/pci/tw68/tw68.h +++ b/kernel/drivers/media/pci/tw68/tw68.h @@ -36,28 +36,13 @@ #include #include #include +#include #include #include "tw68-reg.h" #define UNSET (-1U) -/* system vendor and device ID's */ -#define PCI_VENDOR_ID_TECHWELL 0x1797 -#define PCI_DEVICE_ID_6800 0x6800 -#define PCI_DEVICE_ID_6801 0x6801 -#define PCI_DEVICE_ID_AUDIO2 0x6802 -#define PCI_DEVICE_ID_TS3 0x6803 -#define PCI_DEVICE_ID_6804 0x6804 -#define PCI_DEVICE_ID_AUDIO5 0x6805 -#define PCI_DEVICE_ID_TS6 0x6806 - -/* tw6816 based cards */ -#define PCI_DEVICE_ID_6816_1 0x6810 -#define PCI_DEVICE_ID_6816_2 0x6811 -#define PCI_DEVICE_ID_6816_3 0x6812 -#define PCI_DEVICE_ID_6816_4 0x6813 - #define TW68_NORMS ( \ V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM | \ V4L2_STD_PAL_M | V4L2_STD_PAL_Nc | V4L2_STD_PAL_60) @@ -134,7 +119,7 @@ struct tw68_dev; /* forward delclaration */ /* buffer for one video/vbi/ts frame */ struct tw68_buf { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; unsigned int size; diff --git a/kernel/drivers/media/pci/zoran/zoran.h b/kernel/drivers/media/pci/zoran/zoran.h index 5e040085c..4e7db8939 100644 --- a/kernel/drivers/media/pci/zoran/zoran.h +++ b/kernel/drivers/media/pci/zoran/zoran.h @@ -32,6 +32,8 @@ #define _BUZ_H_ #include +#include +#include struct zoran_sync { unsigned long frame; /* number of buffer that has been free'd */ @@ -216,6 +218,7 @@ struct zoran; /* zoran_fh contains per-open() settings */ struct zoran_fh { + struct v4l2_fh fh; struct zoran *zr; enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ @@ -268,6 +271,7 @@ struct card_info { struct zoran { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct video_device *video_dev; struct i2c_adapter i2c_adapter; /* */ @@ -280,8 +284,7 @@ struct zoran { struct videocodec *codec; /* video codec */ struct videocodec *vfe; /* video front end */ - struct mutex resource_lock; /* prevent evil stuff */ - struct mutex other_lock; /* please merge with above */ + struct mutex lock; /* file ops serialize lock */ u8 initialized; /* flag if zoran has been correctly initialized */ int user; /* number of current users */ diff --git a/kernel/drivers/media/pci/zoran/zoran_card.c b/kernel/drivers/media/pci/zoran/zoran_card.c index cec5b7553..1136d92af 100644 --- a/kernel/drivers/media/pci/zoran/zoran_card.c +++ b/kernel/drivers/media/pci/zoran/zoran_card.c @@ -1049,8 +1049,9 @@ static int zr36057_init (struct zoran *zr) /* * Now add the template and register the device unit. */ - memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); + *zr->video_dev = zoran_template; zr->video_dev->v4l2_dev = &zr->v4l2_dev; + zr->video_dev->lock = &zr->lock; strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); /* It's not a mem2mem device, but you can both capture and output from one and the same device. This should really be split up into two @@ -1116,6 +1117,7 @@ static void zoran_remove(struct pci_dev *pdev) pci_disable_device(zr->pci_dev); video_unregister_device(zr->video_dev); exit_free: + v4l2_ctrl_handler_free(&zr->hdl); v4l2_device_unregister(&zr->v4l2_dev); kfree(zr); } @@ -1219,9 +1221,11 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) zr->pci_dev = pdev; zr->id = nr; snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); + if (v4l2_ctrl_handler_init(&zr->hdl, 10)) + goto zr_unreg; + zr->v4l2_dev.ctrl_handler = &zr->hdl; spin_lock_init(&zr->spinlock); - mutex_init(&zr->resource_lock); - mutex_init(&zr->other_lock); + mutex_init(&zr->lock); if (pci_enable_device(pdev)) goto zr_unreg; zr->revision = zr->pci_dev->revision; @@ -1443,6 +1447,7 @@ zr_free_irq: zr_unmap: iounmap(zr->zr36057_mem); zr_unreg: + v4l2_ctrl_handler_free(&zr->hdl); v4l2_device_unregister(&zr->v4l2_dev); zr_free_mem: kfree(zr); diff --git a/kernel/drivers/media/pci/zoran/zoran_device.c b/kernel/drivers/media/pci/zoran/zoran_device.c index b6801e035..4d47ddac9 100644 --- a/kernel/drivers/media/pci/zoran/zoran_device.c +++ b/kernel/drivers/media/pci/zoran/zoran_device.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -181,20 +182,11 @@ dump_guests (struct zoran *zr) } } -static inline unsigned long -get_time (void) -{ - struct timeval tv; - - do_gettimeofday(&tv); - return (1000000 * tv.tv_sec + tv.tv_usec); -} - void detect_guest_activity (struct zoran *zr) { int timeout, i, j, res, guest[8], guest0[8], change[8][3]; - unsigned long t0, t1; + ktime_t t0, t1; dump_guests(zr); printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", @@ -205,15 +197,15 @@ detect_guest_activity (struct zoran *zr) timeout = 0; j = 0; - t0 = get_time(); + t0 = ktime_get(); while (timeout < 10000) { udelay(10); timeout++; for (i = 1; (i < 8) && (j < 8); i++) { res = post_office_read(zr, i, 0); if (res != guest[i]) { - t1 = get_time(); - change[j][0] = (t1 - t0); + t1 = ktime_get(); + change[j][0] = ktime_to_us(ktime_sub(t1, t0)); t0 = t1; change[j][1] = i; change[j][2] = res; @@ -1584,14 +1576,11 @@ zoran_init_hardware (struct zoran *zr) jpeg_codec_sleep(zr, 1); jpeg_codec_sleep(zr, 0); - /* set individual interrupt enables (without GIRQ1) - * but don't global enable until zoran_open() */ - - //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW - // It looks like using only JPEGRepIRQEn is not always reliable, - // may be when JPEG codec crashes it won't generate IRQ? So, - /*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP - zr36057_init_vfe(zr); + /* + * set individual interrupt enables (without GIRQ1) + * but don't global enable until zoran_open() + */ + zr36057_init_vfe(zr); zr36057_enable_jpg(zr, BUZ_MODE_IDLE); diff --git a/kernel/drivers/media/pci/zoran/zoran_driver.c b/kernel/drivers/media/pci/zoran/zoran_driver.c index 2b25d31c4..80caa70c6 100644 --- a/kernel/drivers/media/pci/zoran/zoran_driver.c +++ b/kernel/drivers/media/pci/zoran/zoran_driver.c @@ -61,6 +61,7 @@ #include #include #include +#include #include "videocodec.h" #include @@ -592,10 +593,14 @@ static int v4l_sync(struct zoran_fh *fh, int frame) return -EPROTO; } + mutex_unlock(&zr->lock); /* wait on this buffer to get ready */ if (!wait_event_interruptible_timeout(zr->v4l_capq, - (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) + (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) { + mutex_lock(&zr->lock); return -ETIME; + } + mutex_lock(&zr->lock); if (signal_pending(current)) return -ERESTARTSYS; @@ -783,6 +788,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) ZR_DEVNAME(zr), __func__); return -EINVAL; } + mutex_unlock(&zr->lock); if (!wait_event_interruptible_timeout(zr->jpg_capq, (zr->jpg_que_tail != zr->jpg_dma_tail || zr->jpg_dma_tail == zr->jpg_dma_head), @@ -793,6 +799,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) udelay(1); zr->codec->control(zr->codec, CODEC_G_STATUS, sizeof(isr), &isr); + mutex_lock(&zr->lock); dprintk(1, KERN_ERR "%s: %s - timeout: codec isr=0x%02x\n", @@ -801,6 +808,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) return -ETIME; } + mutex_lock(&zr->lock); if (signal_pending(current)) return -ERESTARTSYS; @@ -911,7 +919,7 @@ static int zoran_open(struct file *file) dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); - mutex_lock(&zr->other_lock); + mutex_lock(&zr->lock); if (zr->user >= 2048) { dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", @@ -930,6 +938,8 @@ static int zoran_open(struct file *file) res = -ENOMEM; goto fail_unlock; } + v4l2_fh_init(&fh->fh, video_devdata(file)); + /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows * on norm-change! */ fh->overlay_mask = @@ -946,8 +956,6 @@ static int zoran_open(struct file *file) if (zr->user++ == 0) first_open = 1; - /*mutex_unlock(&zr->resource_lock);*/ - /* default setup - TODO: look at flags */ if (first_open) { /* First device open */ zr36057_restart(zr); @@ -961,14 +969,15 @@ static int zoran_open(struct file *file) file->private_data = fh; fh->zr = zr; zoran_open_init_session(fh); - mutex_unlock(&zr->other_lock); + v4l2_fh_add(&fh->fh); + mutex_unlock(&zr->lock); return 0; fail_fh: kfree(fh); fail_unlock: - mutex_unlock(&zr->other_lock); + mutex_unlock(&zr->lock); dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n", ZR_DEVNAME(zr), res, zr->user); @@ -987,7 +996,7 @@ zoran_close(struct file *file) /* kernel locks (fs/device.c), so don't do that ourselves * (prevents deadlocks) */ - mutex_lock(&zr->other_lock); + mutex_lock(&zr->lock); zoran_close_end_session(fh); @@ -1021,9 +1030,10 @@ zoran_close(struct file *file) encoder_call(zr, video, s_routing, 2, 0, 0); } } - mutex_unlock(&zr->other_lock); + mutex_unlock(&zr->lock); - file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh->overlay_mask); kfree(fh); @@ -1032,29 +1042,6 @@ zoran_close(struct file *file) return 0; } - -static ssize_t -zoran_read (struct file *file, - char __user *data, - size_t count, - loff_t *ppos) -{ - /* we simply don't support read() (yet)... */ - - return -EINVAL; -} - -static ssize_t -zoran_write (struct file *file, - const char __user *data, - size_t count, - loff_t *ppos) -{ - /* ...and the same goes for write() */ - - return -EINVAL; -} - static int setup_fbuffer(struct zoran_fh *fh, void *base, const struct zoran_format *fmt, @@ -1523,7 +1510,6 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - memset(cap, 0, sizeof(*cap)); strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", @@ -1583,9 +1569,6 @@ static int zoran_g_fmt_vid_out(struct file *file, void *__fh, struct v4l2_format *fmt) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - mutex_lock(&zr->resource_lock); fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm; fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 / @@ -1601,7 +1584,6 @@ static int zoran_g_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1614,7 +1596,6 @@ static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, if (fh->map_mode != ZORAN_MAP_MODE_RAW) return zoran_g_fmt_vid_out(file, fh, fmt); - mutex_lock(&zr->resource_lock); fmt->fmt.pix.width = fh->v4l_settings.width; fmt->fmt.pix.height = fh->v4l_settings.height; fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline * @@ -1626,7 +1607,6 @@ static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; else fmt->fmt.pix.field = V4L2_FIELD_TOP; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1636,8 +1616,6 @@ static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); - fmt->fmt.win.w.left = fh->overlay_settings.x; fmt->fmt.win.w.top = fh->overlay_settings.y; fmt->fmt.win.w.width = fh->overlay_settings.width; @@ -1647,7 +1625,6 @@ static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, else fmt->fmt.win.field = V4L2_FIELD_TOP; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1657,8 +1634,6 @@ static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); - if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) fmt->fmt.win.w.width = BUZ_MAX_WIDTH; if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) @@ -1668,7 +1643,6 @@ static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1683,7 +1657,6 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; - mutex_lock(&zr->resource_lock); settings = fh->jpg_settings; /* we actually need to set 'real' parameters now */ @@ -1718,7 +1691,7 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, /* check */ res = zoran_check_jpg_settings(zr, &settings, 1); if (res) - goto tryfmt_unlock_and_return; + return res; /* tell the user what we actually did */ fmt->fmt.pix.width = settings.img_width / settings.HorDcm; @@ -1734,8 +1707,6 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -tryfmt_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1750,23 +1721,17 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) return zoran_try_fmt_vid_out(file, fh, fmt); - mutex_lock(&zr->resource_lock); - for (i = 0; i < NUM_FORMATS; i++) if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) break; - if (i == NUM_FORMATS) { - mutex_unlock(&zr->resource_lock); + if (i == NUM_FORMATS) return -EINVAL; - } bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); v4l_bound_align_image( &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); - mutex_unlock(&zr->resource_lock); - return 0; } @@ -1774,7 +1739,6 @@ static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *fmt) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", @@ -1783,12 +1747,10 @@ static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, fmt->fmt.win.w.height, fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); - mutex_lock(&zr->resource_lock); res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, fmt->fmt.win.w.width, fmt->fmt.win.w.height, (struct v4l2_clip __user *)fmt->fmt.win.clips, fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); - mutex_unlock(&zr->resource_lock); return res; } @@ -1808,13 +1770,11 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; - mutex_lock(&zr->resource_lock); - if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sfmtjpg_unlock_and_return; + return res; } settings = fh->jpg_settings; @@ -1851,7 +1811,7 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, /* check */ res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto sfmtjpg_unlock_and_return; + return res; /* it's ok, so set them */ fh->jpg_settings = settings; @@ -1872,9 +1832,6 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = fh->buffers.buffer_size; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - -sfmtjpg_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1898,14 +1855,12 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, return -EINVAL; } - mutex_lock(&zr->resource_lock); - if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sfmtv4l_unlock_and_return; + return res; } if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) fmt->fmt.pix.height = BUZ_MAX_HEIGHT; @@ -1917,7 +1872,7 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, &zoran_formats[i]); if (res) - goto sfmtv4l_unlock_and_return; + return res; /* tell the user the results/missing stuff */ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; @@ -1927,9 +1882,6 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; else fmt->fmt.pix.field = V4L2_FIELD_TOP; - -sfmtv4l_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1940,14 +1892,12 @@ static int zoran_g_fbuf(struct file *file, void *__fh, struct zoran *zr = fh->zr; memset(fb, 0, sizeof(*fb)); - mutex_lock(&zr->resource_lock); fb->base = zr->vbuf_base; fb->fmt.width = zr->vbuf_width; fb->fmt.height = zr->vbuf_height; if (zr->overlay_settings.format) fb->fmt.pixelformat = fh->overlay_settings.format->fourcc; fb->fmt.bytesperline = zr->vbuf_bytesperline; - mutex_unlock(&zr->resource_lock); fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; fb->fmt.field = V4L2_FIELD_INTERLACED; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; @@ -1973,10 +1923,8 @@ static int zoran_s_fbuf(struct file *file, void *__fh, return -EINVAL; } - mutex_lock(&zr->resource_lock); res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, fb->fmt.height, fb->fmt.bytesperline); - mutex_unlock(&zr->resource_lock); return res; } @@ -1984,12 +1932,9 @@ static int zoran_s_fbuf(struct file *file, void *__fh, static int zoran_overlay(struct file *file, void *__fh, unsigned int on) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = setup_overlay(fh, on); - mutex_unlock(&zr->resource_lock); return res; } @@ -2013,14 +1958,13 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (req->count == 0) return zoran_streamoff(file, fh, req->type); - mutex_lock(&zr->resource_lock); if (fh->buffers.allocated) { dprintk(2, KERN_ERR "%s: VIDIOC_REQBUFS - buffers already allocated\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto v4l2reqbuf_unlock_and_return; + return res; } if (fh->map_mode == ZORAN_MAP_MODE_RAW && @@ -2037,7 +1981,7 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (v4l_fbuffer_alloc(fh)) { res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; + return res; } } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { @@ -2054,7 +1998,7 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (jpg_fbuffer_alloc(fh)) { res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; + return res; } } else { dprintk(1, @@ -2062,23 +2006,17 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe "%s: VIDIOC_REQBUFS - unknown type %d\n", ZR_DEVNAME(zr), req->type); res = -EINVAL; - goto v4l2reqbuf_unlock_and_return; + return res; } -v4l2reqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = zoran_v4l2_buffer_status(fh, buf, buf->index); - mutex_unlock(&zr->resource_lock); return res; } @@ -2089,8 +2027,6 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) struct zoran *zr = fh->zr; int res = 0, codec_mode, buf_type; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -2098,12 +2034,12 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto qbuf_unlock_and_return; + return res; } res = zoran_v4l_queue_frame(fh, buf->index); if (res) - goto qbuf_unlock_and_return; + return res; if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) zr36057_set_memgrab(zr, 1); break; @@ -2123,12 +2059,12 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto qbuf_unlock_and_return; + return res; } res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); if (res != 0) - goto qbuf_unlock_and_return; + return res; if (zr->codec_mode == BUZ_MODE_IDLE && fh->buffers.active == ZORAN_LOCKED) zr36057_enable_jpg(zr, codec_mode); @@ -2142,9 +2078,6 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EINVAL; break; } -qbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2154,8 +2087,6 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) struct zoran *zr = fh->zr; int res = 0, buf_type, num = -1; /* compiler borks here (?) */ - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -2163,18 +2094,18 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto dqbuf_unlock_and_return; + return res; } num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; if (file->f_flags & O_NONBLOCK && zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) { res = -EAGAIN; - goto dqbuf_unlock_and_return; + return res; } res = v4l_sync(fh, num); if (res) - goto dqbuf_unlock_and_return; + return res; zr->v4l_sync_tail++; res = zoran_v4l2_buffer_status(fh, buf, num); break; @@ -2194,7 +2125,7 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto dqbuf_unlock_and_return; + return res; } num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; @@ -2202,12 +2133,12 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) if (file->f_flags & O_NONBLOCK && zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) { res = -EAGAIN; - goto dqbuf_unlock_and_return; + return res; } bs.frame = 0; /* suppress compiler warning */ res = jpg_sync(fh, &bs); if (res) - goto dqbuf_unlock_and_return; + return res; res = zoran_v4l2_buffer_status(fh, buf, bs.frame); break; } @@ -2219,9 +2150,6 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EINVAL; break; } -dqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2231,14 +2159,12 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type struct zoran *zr = fh->zr; int res = 0; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ if (zr->v4l_buffers.active != ZORAN_ACTIVE || fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; - goto strmon_unlock_and_return; + return res; } zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; @@ -2257,7 +2183,7 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type if (zr->jpg_buffers.active != ZORAN_ACTIVE || fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; - goto strmon_unlock_and_return; + return res; } zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; @@ -2276,9 +2202,6 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type res = -EINVAL; break; } -strmon_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2289,17 +2212,15 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ int i, res = 0; unsigned long flags; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ if (fh->buffers.active == ZORAN_FREE && zr->v4l_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; + return res; } if (zr->v4l_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; + return res; spin_lock_irqsave(&zr->spinlock, flags); /* unload capture */ @@ -2327,17 +2248,17 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ if (fh->buffers.active == ZORAN_FREE && zr->jpg_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; + return res; } if (zr->jpg_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; + return res; res = jpg_qbuf(fh, -1, (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? BUZ_MODE_MOTION_COMPRESS : BUZ_MODE_MOTION_DECOMPRESS); if (res) - goto strmoff_unlock_and_return; + return res; break; default: dprintk(1, KERN_ERR @@ -2346,70 +2267,14 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ res = -EINVAL; break; } -strmoff_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } - -static int zoran_queryctrl(struct file *file, void *__fh, - struct v4l2_queryctrl *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - decoder_call(zr, core, queryctrl, ctrl); - - return 0; -} - -static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - mutex_lock(&zr->resource_lock); - decoder_call(zr, core, g_ctrl, ctrl); - mutex_unlock(&zr->resource_lock); - - return 0; -} - -static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - mutex_lock(&zr->resource_lock); - decoder_call(zr, core, s_ctrl, ctrl); - mutex_unlock(&zr->resource_lock); - - return 0; -} - static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); *std = zr->norm; - mutex_unlock(&zr->resource_lock); return 0; } @@ -2419,14 +2284,11 @@ static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) struct zoran *zr = fh->zr; int res = 0; - mutex_lock(&zr->resource_lock); res = zoran_set_norm(zr, std); if (res) - goto sstd_unlock_and_return; + return res; res = wait_grab_pending(zr); -sstd_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2445,9 +2307,7 @@ static int zoran_enum_input(struct file *file, void *__fh, inp->std = V4L2_STD_ALL; /* Get status of video decoder */ - mutex_lock(&zr->resource_lock); decoder_call(zr, video, g_input_status, &inp->status); - mutex_unlock(&zr->resource_lock); return 0; } @@ -2456,9 +2316,7 @@ static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); *input = zr->input; - mutex_unlock(&zr->resource_lock); return 0; } @@ -2469,15 +2327,12 @@ static int zoran_s_input(struct file *file, void *__fh, unsigned int input) struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = zoran_set_input(zr, input); if (res) - goto sinput_unlock_and_return; + return res; /* Make sure the changes come into effect */ res = wait_grab_pending(zr); -sinput_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2520,8 +2375,6 @@ static int zoran_cropcap(struct file *file, void *__fh, memset(cropcap, 0, sizeof(*cropcap)); cropcap->type = type; - mutex_lock(&zr->resource_lock); - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || fh->map_mode == ZORAN_MAP_MODE_RAW)) { @@ -2529,7 +2382,7 @@ static int zoran_cropcap(struct file *file, void *__fh, "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto cropcap_unlock_and_return; + return res; } cropcap->bounds.top = cropcap->bounds.left = 0; @@ -2538,8 +2391,6 @@ static int zoran_cropcap(struct file *file, void *__fh, cropcap->defrect.top = cropcap->defrect.left = 0; cropcap->defrect.width = BUZ_MIN_WIDTH; cropcap->defrect.height = BUZ_MIN_HEIGHT; -cropcap_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2552,8 +2403,6 @@ static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) memset(crop, 0, sizeof(*crop)); crop->type = type; - mutex_lock(&zr->resource_lock); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || fh->map_mode == ZORAN_MAP_MODE_RAW)) { @@ -2562,17 +2411,13 @@ static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto gcrop_unlock_and_return; + return res; } crop->c.top = fh->jpg_settings.img_y; crop->c.left = fh->jpg_settings.img_x; crop->c.width = fh->jpg_settings.img_width; crop->c.height = fh->jpg_settings.img_height; - -gcrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2585,14 +2430,12 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c settings = fh->jpg_settings; - mutex_lock(&zr->resource_lock); - if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_CROP - cannot change settings while active\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto scrop_unlock_and_return; + return res; } if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && @@ -2602,7 +2445,7 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto scrop_unlock_and_return; + return res; } /* move into a form that we understand */ @@ -2614,13 +2457,10 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c /* check validity */ res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto scrop_unlock_and_return; + return res; /* accept */ fh->jpg_settings = settings; - -scrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2628,11 +2468,8 @@ static int zoran_g_jpegcomp(struct file *file, void *__fh, struct v4l2_jpegcompression *params) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; memset(params, 0, sizeof(*params)); - mutex_lock(&zr->resource_lock); - params->quality = fh->jpg_settings.jpg_comp.quality; params->APPn = fh->jpg_settings.jpg_comp.APPn; memcpy(params->APP_data, @@ -2646,8 +2483,6 @@ static int zoran_g_jpegcomp(struct file *file, void *__fh, params->jpeg_markers = fh->jpg_settings.jpg_comp.jpeg_markers; - mutex_unlock(&zr->resource_lock); - return 0; } @@ -2663,26 +2498,21 @@ static int zoran_s_jpegcomp(struct file *file, void *__fh, settings.jpg_comp = *params; - mutex_lock(&zr->resource_lock); - if (fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sjpegc_unlock_and_return; + return res; } res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto sjpegc_unlock_and_return; + return res; if (!fh->buffers.allocated) fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); fh->jpg_settings.jpg_comp = settings.jpg_comp; -sjpegc_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2692,7 +2522,8 @@ zoran_poll (struct file *file, { struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; - int res = 0, frame; + int res = v4l2_ctrl_poll(file, wait); + int frame; unsigned long flags; /* we should check whether buffers are ready to be synced on @@ -2703,8 +2534,6 @@ zoran_poll (struct file *file, * if no buffers queued or so, return POLLNVAL */ - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: poll_wait(file, &zr->v4l_capq, wait); @@ -2722,7 +2551,7 @@ zoran_poll (struct file *file, if (fh->buffers.active != ZORAN_FREE && /* Buffer ready to DQBUF? */ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; spin_unlock_irqrestore(&zr->spinlock, flags); break; @@ -2743,9 +2572,9 @@ zoran_poll (struct file *file, if (fh->buffers.active != ZORAN_FREE && zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; else - res = POLLOUT | POLLWRNORM; + res |= POLLOUT | POLLWRNORM; } spin_unlock_irqrestore(&zr->spinlock, flags); @@ -2756,11 +2585,9 @@ zoran_poll (struct file *file, KERN_ERR "%s: %s - internal error, unknown map_mode=%d\n", ZR_DEVNAME(zr), __func__, fh->map_mode); - res = POLLNVAL; + res |= POLLERR; } - mutex_unlock(&zr->resource_lock); - return res; } @@ -2792,9 +2619,6 @@ zoran_vm_close (struct vm_area_struct *vma) struct zoran *zr = fh->zr; int i; - if (!atomic_dec_and_mutex_lock(&map->count, &zr->resource_lock)) - return; - dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); @@ -2807,7 +2631,6 @@ zoran_vm_close (struct vm_area_struct *vma) /* Any buffers still mapped? */ for (i = 0; i < fh->buffers.num_buffers; i++) { if (fh->buffers.buffer[i].map) { - mutex_unlock(&zr->resource_lock); return; } } @@ -2815,7 +2638,6 @@ zoran_vm_close (struct vm_area_struct *vma) dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { if (fh->buffers.active != ZORAN_FREE) { unsigned long flags; @@ -2835,8 +2657,6 @@ zoran_vm_close (struct vm_area_struct *vma) } jpg_fbuffer_free(fh); } - - mutex_unlock(&zr->resource_lock); } static const struct vm_operations_struct zoran_vm_ops = { @@ -2872,15 +2692,13 @@ zoran_mmap (struct file *file, return -EINVAL; } - mutex_lock(&zr->resource_lock); - if (!fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: %s(%s) - buffers not yet allocated\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); res = -ENOMEM; - goto mmap_unlock_and_return; + return res; } first = offset / fh->buffers.buffer_size; @@ -2896,7 +2714,7 @@ zoran_mmap (struct file *file, fh->buffers.buffer_size, fh->buffers.num_buffers); res = -EINVAL; - goto mmap_unlock_and_return; + return res; } /* Check if any buffers are already mapped */ @@ -2907,7 +2725,7 @@ zoran_mmap (struct file *file, "%s: %s(%s) - buffer %d already mapped\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); res = -EBUSY; - goto mmap_unlock_and_return; + return res; } } @@ -2915,7 +2733,7 @@ zoran_mmap (struct file *file, map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); if (!map) { res = -ENOMEM; - goto mmap_unlock_and_return; + return res; } map->fh = fh; atomic_set(&map->count, 1); @@ -2937,7 +2755,7 @@ zoran_mmap (struct file *file, "%s: %s(V4L) - remap_pfn_range failed\n", ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto mmap_unlock_and_return; + return res; } size -= todo; start += todo; @@ -2969,7 +2787,7 @@ zoran_mmap (struct file *file, "%s: %s(V4L) - remap_pfn_range failed\n", ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto mmap_unlock_and_return; + return res; } size -= todo; start += todo; @@ -2985,10 +2803,6 @@ zoran_mmap (struct file *file, } } - -mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -3028,33 +2842,15 @@ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out, .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay, - .vidioc_queryctrl = zoran_queryctrl, - .vidioc_s_ctrl = zoran_s_ctrl, - .vidioc_g_ctrl = zoran_g_ctrl, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -/* please use zr->resource_lock consistently and kill this wrapper */ -static long zoran_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - int ret; - - mutex_lock(&zr->other_lock); - ret = video_ioctl2(file, cmd, arg); - mutex_unlock(&zr->other_lock); - - return ret; -} - static const struct v4l2_file_operations zoran_fops = { .owner = THIS_MODULE, .open = zoran_open, .release = zoran_close, - .unlocked_ioctl = zoran_ioctl, - .read = zoran_read, - .write = zoran_write, + .unlocked_ioctl = video_ioctl2, .mmap = zoran_mmap, .poll = zoran_poll, }; diff --git a/kernel/drivers/media/platform/Kconfig b/kernel/drivers/media/platform/Kconfig index 421f53188..ccbc9742c 100644 --- a/kernel/drivers/media/platform/Kconfig +++ b/kernel/drivers/media/platform/Kconfig @@ -1,6 +1,6 @@ # # Platform drivers -# All drivers here are currently for webcam support +# Most drivers here are currently for webcam support menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" @@ -86,9 +86,9 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP3 tristate "OMAP 3 Camera support" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 - depends on HAS_DMA + depends on HAS_DMA && OF + depends on OMAP_IOMMU select ARM_DMA_USE_IOMMU - select OMAP_IOMMU select VIDEOBUF2_DMA_CONTIG select MFD_SYSCON ---help--- @@ -212,6 +212,16 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_STI_BDISP + tristate "STMicroelectronics BDISP 2D blitter driver" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_STI || COMPILE_TEST + depends on HAVE_DMA_ATTRS + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. + config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA @@ -221,6 +231,18 @@ config VIDEO_SH_VEU Support for the Video Engine Unit (VEU) on SuperH and SH-Mobile SoCs. +config VIDEO_RENESAS_JPU + tristate "Renesas JPEG Processing Unit" + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on ARCH_SHMOBILE || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + ---help--- + This is a V4L2 driver for the Renesas JPEG Processing Unit. + + To compile this driver as a module, choose M here: the module + will be called rcar_jpu. + config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA @@ -270,3 +292,14 @@ config VIDEO_VIM2M This is a virtual test device for the memory-to-memory driver framework. endif #V4L_TEST_DRIVERS + +menuconfig DVB_PLATFORM_DRIVERS + bool "DVB platform devices" + depends on MEDIA_DIGITAL_TV_SUPPORT + default n + ---help--- + Say Y here to enable support for platform-specific Digital TV drivers. + +if DVB_PLATFORM_DRIVERS +source "drivers/media/platform/sti/c8sectpfe/Kconfig" +endif #DVB_PLATFORM_DRIVERS diff --git a/kernel/drivers/media/platform/Makefile b/kernel/drivers/media/platform/Makefile index 8f855616c..efa0295af 100644 --- a/kernel/drivers/media/platform/Makefile +++ b/kernel/drivers/media/platform/Makefile @@ -34,6 +34,9 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ +obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ +obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ + obj-$(CONFIG_BLACKFIN) += blackfin/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ @@ -42,6 +45,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ obj-y += omap/ diff --git a/kernel/drivers/media/platform/am437x/am437x-vpfe.c b/kernel/drivers/media/platform/am437x/am437x-vpfe.c index ddf59ee5c..f0480d687 100644 --- a/kernel/drivers/media/platform/am437x/am437x-vpfe.c +++ b/kernel/drivers/media/platform/am437x/am437x-vpfe.c @@ -288,7 +288,8 @@ cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs) lhs->fmt.pix.field == rhs->fmt.pix.field && lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace && lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc && - lhs->fmt.pix.quantization == rhs->fmt.pix.quantization; + lhs->fmt.pix.quantization == rhs->fmt.pix.quantization && + lhs->fmt.pix.xfer_func == rhs->fmt.pix.xfer_func; } static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset) @@ -306,7 +307,8 @@ static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc) return container_of(ccdc, struct vpfe_device, ccdc); } -static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb) +static inline +struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct vpfe_cap_buffer, vb); } @@ -430,7 +432,7 @@ vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc, struct vpfe_ccdc_config_params_raw *config_params = &ccdc->ccdc_cfg.bayer.config_params; - config_params = raw_params; + *config_params = *raw_params; } /* @@ -510,7 +512,7 @@ static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) { vpfe_ccdc_update_raw_params(ccdc, &raw_params); - return 0; + return 0; } return -EINVAL; @@ -1095,7 +1097,7 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) * For a given standard, this functions sets up the default * pix format & crop values in the vpfe device and ccdc. It first * starts with defaults based values from the standard table. - * It then checks if sub device support g_mbus_fmt and then override the + * It then checks if sub device supports get_fmt and then override the * values based on that.Sets crop values to match with scan resolution * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the * values in ccdc @@ -1256,14 +1258,14 @@ static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) list_del(&vpfe->next_frm->list); vpfe_set_sdr_addr(&vpfe->ccdc, - vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0)); + vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0)); } static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) { unsigned long addr; - addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) + + addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0) + vpfe->field_off; vpfe_set_sdr_addr(&vpfe->ccdc, addr); @@ -1279,10 +1281,10 @@ static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) */ static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) { - v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp); - vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field; - vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++; - vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vpfe->cur_frm->vb.timestamp); + vpfe->cur_frm->vb.field = vpfe->fmt.fmt.pix.field; + vpfe->cur_frm->vb.sequence = vpfe->sequence++; + vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); vpfe->cur_frm = vpfe->next_frm; } @@ -1442,8 +1444,8 @@ static int __vpfe_get_format(struct vpfe_device *vpfe, } else { ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, - video, g_mbus_fmt, - &mbus_fmt); + pad, get_fmt, + NULL, &fmt); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) return ret; v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); @@ -1465,7 +1467,6 @@ static int __vpfe_get_format(struct vpfe_device *vpfe, static int __vpfe_set_format(struct vpfe_device *vpfe, struct v4l2_format *format, unsigned int *bpp) { - struct v4l2_mbus_framefmt mbus_fmt; struct vpfe_subdev_info *sdinfo; struct v4l2_subdev_format fmt; int ret; @@ -1482,23 +1483,11 @@ static int __vpfe_set_format(struct vpfe_device *vpfe, pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format); ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + if (ret) return ret; - if (!ret) { - v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); - mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); - } else { - ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, - sdinfo->grp_id, - video, s_mbus_fmt, - &mbus_fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); - mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); - } + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); format->type = vpfe->fmt.type; @@ -1685,12 +1674,9 @@ vpfe_get_subdev_input_index(struct vpfe_device *vpfe, int *subdev_input_index, int app_input_index) { - struct vpfe_config *cfg = vpfe->cfg; - struct vpfe_subdev_info *sdinfo; int i, j = 0; for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { - sdinfo = &cfg->sub_devs[i]; if (app_input_index < (j + 1)) { *subdev_index = i; *subdev_input_index = app_input_index - j; @@ -1922,10 +1908,11 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe) * the buffer count and buffer size */ static int vpfe_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct vpfe_device *vpfe = vb2_get_drv_priv(vq); if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage) @@ -1957,6 +1944,7 @@ static int vpfe_queue_setup(struct vb2_queue *vq, */ static int vpfe_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage); @@ -1964,7 +1952,7 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb) if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) return -EINVAL; - vb->v4l2_buf.field = vpfe->fmt.fmt.pix.field; + vbuf->field = vpfe->fmt.fmt.pix.field; return 0; } @@ -1975,8 +1963,9 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb) */ static void vpfe_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); - struct vpfe_cap_buffer *buf = to_vpfe_buffer(vb); + struct vpfe_cap_buffer *buf = to_vpfe_buffer(vbuf); unsigned long flags = 0; /* add the buffer to the DMA queue */ @@ -2021,7 +2010,7 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) list_del(&vpfe->cur_frm->list); spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); - addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb.vb2_buf, 0); vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr)); @@ -2038,7 +2027,7 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) err: list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } return ret; @@ -2070,13 +2059,14 @@ static void vpfe_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ spin_lock_irqsave(&vpfe->dma_queue_lock, flags); if (vpfe->cur_frm == vpfe->next_frm) { - vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } else { if (vpfe->cur_frm != NULL) - vb2_buffer_done(&vpfe->cur_frm->vb, + vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); if (vpfe->next_frm != NULL) - vb2_buffer_done(&vpfe->next_frm->vb, + vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); } @@ -2084,7 +2074,8 @@ static void vpfe_stop_streaming(struct vb2_queue *vq) vpfe->next_frm = list_entry(vpfe->dma_queue.next, struct vpfe_cap_buffer, list); list_del(&vpfe->next_frm->list); - vb2_buffer_done(&vpfe->next_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); } @@ -2561,11 +2552,12 @@ static int vpfe_probe(struct platform_device *pdev) if (IS_ERR(ccdc->ccdc_cfg.base_addr)) return PTR_ERR(ccdc->ccdc_cfg.base_addr); - vpfe->irq = platform_get_irq(pdev, 0); - if (vpfe->irq <= 0) { + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { dev_err(&pdev->dev, "No IRQ resource\n"); return -ENODEV; } + vpfe->irq = ret; ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, "vpfe_capture0", vpfe); diff --git a/kernel/drivers/media/platform/am437x/am437x-vpfe.h b/kernel/drivers/media/platform/am437x/am437x-vpfe.h index 5bfb35649..777bf97fe 100644 --- a/kernel/drivers/media/platform/am437x/am437x-vpfe.h +++ b/kernel/drivers/media/platform/am437x/am437x-vpfe.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "am437x-vpfe_regs.h" @@ -104,7 +105,7 @@ struct vpfe_config { }; struct vpfe_cap_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; diff --git a/kernel/drivers/media/platform/blackfin/bfin_capture.c b/kernel/drivers/media/platform/blackfin/bfin_capture.c index 6a437f86d..7764b9c48 100644 --- a/kernel/drivers/media/platform/blackfin/bfin_capture.c +++ b/kernel/drivers/media/platform/blackfin/bfin_capture.c @@ -54,7 +54,7 @@ struct bcap_format { }; struct bcap_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -149,21 +149,25 @@ static const struct bcap_format bcap_formats[] = { static irqreturn_t bcap_isr(int irq, void *dev_id); -static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb) +static struct bcap_buffer *to_bcap_vb(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct bcap_buffer, vb); } static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) { - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct bcap_format *sf; unsigned int num_formats = 0; int i, j; - while (!v4l2_subdev_call(bcap_dev->sd, video, - enum_mbus_fmt, num_formats, &code)) + while (!v4l2_subdev_call(bcap_dev->sd, pad, + enum_mbus_code, NULL, &code)) { num_formats++; + code.index++; + } if (!num_formats) return -ENXIO; @@ -172,10 +176,11 @@ static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) return -ENOMEM; for (i = 0; i < num_formats; i++) { - v4l2_subdev_call(bcap_dev->sd, video, - enum_mbus_fmt, i, &code); + code.index = i; + v4l2_subdev_call(bcap_dev->sd, pad, + enum_mbus_code, NULL, &code); for (j = 0; j < BCAP_MAX_FMTS; j++) - if (code == bcap_formats[j].mbus_code) + if (code.code == bcap_formats[j].mbus_code) break; if (j == BCAP_MAX_FMTS) { /* we don't allow this sensor working with our bridge */ @@ -197,10 +202,11 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) } static int bcap_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); if (fmt && fmt->fmt.pix.sizeimage < bcap_dev->fmt.sizeimage) @@ -218,6 +224,7 @@ static int bcap_queue_setup(struct vb2_queue *vq, static int bcap_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size = bcap_dev->fmt.sizeimage; @@ -228,15 +235,16 @@ static int bcap_buffer_prepare(struct vb2_buffer *vb) } vb2_set_plane_payload(vb, 0, size); - vb->v4l2_buf.field = bcap_dev->fmt.field; + vbuf->field = bcap_dev->fmt.field; return 0; } static void bcap_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - struct bcap_buffer *buf = to_bcap_vb(vb); + struct bcap_buffer *buf = to_bcap_vb(vbuf); unsigned long flags; spin_lock_irqsave(&bcap_dev->lock, flags); @@ -246,8 +254,9 @@ static void bcap_buffer_queue(struct vb2_buffer *vb) static void bcap_buffer_cleanup(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); - struct bcap_buffer *buf = to_bcap_vb(vb); + struct bcap_buffer *buf = to_bcap_vb(vbuf); unsigned long flags; spin_lock_irqsave(&bcap_dev->lock, flags); @@ -328,7 +337,8 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) struct bcap_buffer, list); /* remove buffer from the dma queue */ list_del_init(&bcap_dev->cur_frm->list); - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb.vb2_buf, + 0); /* update DMA address */ ppi->ops->update_addr(ppi, (unsigned long)addr); /* enable ppi */ @@ -339,7 +349,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) err: list_for_each_entry_safe(buf, tmp, &bcap_dev->dma_queue, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } return ret; @@ -362,13 +372,15 @@ static void bcap_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ if (bcap_dev->cur_frm) - vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); while (!list_empty(&bcap_dev->dma_queue)) { bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); list_del_init(&bcap_dev->cur_frm->list); - vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } } @@ -387,18 +399,19 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; struct bcap_device *bcap_dev = ppi->priv; - struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; + struct vb2_v4l2_buffer *vbuf = &bcap_dev->cur_frm->vb; + struct vb2_buffer *vb = &vbuf->vb2_buf; dma_addr_t addr; spin_lock(&bcap_dev->lock); if (!list_empty(&bcap_dev->dma_queue)) { - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vbuf->timestamp); if (ppi->err) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); ppi->err = false; } else { - vb->v4l2_buf.sequence = bcap_dev->sequence++; + vbuf->sequence = bcap_dev->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, @@ -415,7 +428,8 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) if (bcap_dev->stop) { complete(&bcap_dev->comp); } else { - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr( + &bcap_dev->cur_frm->vb.vb2_buf, 0); ppi->ops->update_addr(ppi, (unsigned long)addr); ppi->ops->start(ppi); } @@ -597,7 +611,10 @@ static int bcap_try_format(struct bcap_device *bcap, { struct bcap_format *sf = bcap->sensor_formats; struct bcap_format *fmt = NULL; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; int ret, i; for (i = 0; i < bcap->num_sensor_formats; i++) { @@ -608,16 +625,16 @@ static int bcap_try_format(struct bcap_device *bcap, if (i == bcap->num_sensor_formats) fmt = &sf[0]; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); - ret = v4l2_subdev_call(bcap->sd, video, - try_mbus_fmt, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, pixfmt, fmt->mbus_code); + ret = v4l2_subdev_call(bcap->sd, pad, set_fmt, &pad_cfg, + &format); if (ret < 0) return ret; - v4l2_fill_pix_format(pixfmt, &mbus_fmt); + v4l2_fill_pix_format(pixfmt, &format.format); if (bcap_fmt) { for (i = 0; i < bcap->num_sensor_formats; i++) { fmt = &sf[i]; - if (mbus_fmt.code == fmt->mbus_code) + if (format.format.code == fmt->mbus_code) break; } *bcap_fmt = *fmt; @@ -666,7 +683,9 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct bcap_device *bcap_dev = video_drvdata(file); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct bcap_format bcap_fmt; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; int ret; @@ -679,8 +698,8 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); - ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, pixfmt, bcap_fmt.mbus_code); + ret = v4l2_subdev_call(bcap_dev->sd, pad, set_fmt, NULL, &format); if (ret < 0) return ret; bcap_dev->fmt = *pixfmt; diff --git a/kernel/drivers/media/platform/coda/Makefile b/kernel/drivers/media/platform/coda/Makefile index 834e504bf..9342ac57b 100644 --- a/kernel/drivers/media/platform/coda/Makefile +++ b/kernel/drivers/media/platform/coda/Makefile @@ -1,5 +1,5 @@ ccflags-y += -I$(src) -coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o +coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-jpeg.o obj-$(CONFIG_VIDEO_CODA) += coda.o diff --git a/kernel/drivers/media/platform/coda/coda-bit.c b/kernel/drivers/media/platform/coda/coda-bit.c index d0430071d..654e964f8 100644 --- a/kernel/drivers/media/platform/coda/coda-bit.c +++ b/kernel/drivers/media/platform/coda/coda-bit.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -179,31 +179,32 @@ static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) } static int coda_bitstream_queue(struct coda_ctx *ctx, - struct vb2_buffer *src_buf) + struct vb2_v4l2_buffer *src_buf) { - u32 src_size = vb2_get_plane_payload(src_buf, 0); + u32 src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); u32 n; - n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), - src_size); + n = kfifo_in(&ctx->bitstream_fifo, + vb2_plane_vaddr(&src_buf->vb2_buf, 0), src_size); if (n < src_size) return -ENOSPC; - src_buf->v4l2_buf.sequence = ctx->qsequence++; + src_buf->sequence = ctx->qsequence++; return 0; } static bool coda_bitstream_try_queue(struct coda_ctx *ctx, - struct vb2_buffer *src_buf) + struct vb2_v4l2_buffer *src_buf) { int ret; if (coda_get_bitstream_payload(ctx) + - vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size) + vb2_get_plane_payload(&src_buf->vb2_buf, 0) + 512 >= + ctx->bitstream.size) return false; - if (vb2_plane_vaddr(src_buf, 0) == NULL) { + if (vb2_plane_vaddr(&src_buf->vb2_buf, 0) == NULL) { v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); return true; } @@ -224,10 +225,14 @@ static bool coda_bitstream_try_queue(struct coda_ctx *ctx, void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) { - struct vb2_buffer *src_buf; + struct vb2_v4l2_buffer *src_buf; struct coda_buffer_meta *meta; + unsigned long flags; u32 start; + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) + return; + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { /* * Only queue a single JPEG into the bitstream buffer, except @@ -252,6 +257,13 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) continue; } + /* Dump empty buffers */ + if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) { + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + continue; + } + /* Buffer start position */ start = ctx->bitstream_fifo.kfifo.in & ctx->bitstream_fifo.kfifo.mask; @@ -265,14 +277,19 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) meta = kmalloc(sizeof(*meta), GFP_KERNEL); if (meta) { - meta->sequence = src_buf->v4l2_buf.sequence; - meta->timecode = src_buf->v4l2_buf.timecode; - meta->timestamp = src_buf->v4l2_buf.timestamp; + meta->sequence = src_buf->sequence; + meta->timecode = src_buf->timecode; + meta->timestamp = src_buf->timestamp; meta->start = start; meta->end = ctx->bitstream_fifo.kfifo.in & ctx->bitstream_fifo.kfifo.mask; + spin_lock_irqsave(&ctx->buffer_meta_lock, + flags); list_add_tail(&meta->list, &ctx->buffer_meta_list); + ctx->num_metas++; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, + flags); trace_coda_bit_queue(ctx, src_buf, meta); } @@ -331,7 +348,6 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, { struct coda_dev *dev = ctx->dev; int width, height; - dma_addr_t paddr; int ysize; int ret; int i; @@ -351,7 +367,10 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, size_t size; char *name; - size = ysize + ysize / 2; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + size = round_up(ysize, 4096) + ysize / 2; + else + size = ysize + ysize / 2; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) size += ysize / 4; @@ -367,11 +386,23 @@ 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++) { - paddr = ctx->internal_frames[i].paddr; + u32 y, cb, cr; + /* Start addresses of Y, Cb, Cr planes */ - coda_parabuf_write(ctx, i * 3 + 0, paddr); - coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); - coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4); + y = ctx->internal_frames[i].paddr; + cb = y + ysize; + cr = y + ysize + ysize/4; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { + cb = round_up(cb, 4096); + cr = 0; + /* Packed 20-bit MSB of base addresses */ + /* YYYYYCCC, CCyyyyyc, cccc.... */ + y = (y & 0xfffff000) | cb >> 20; + cb = (cb & 0x000ff000) << 12; + } + coda_parabuf_write(ctx, i * 3 + 0, y); + coda_parabuf_write(ctx, i * 3 + 1, cb); + coda_parabuf_write(ctx, i * 3 + 2, cr); /* mvcol buffer for h.264 */ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && @@ -384,7 +415,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, /* mvcol buffer for mpeg4 */ if ((dev->devtype->product != CODA_DX6) && (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4)) - coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr + + coda_parabuf_write(ctx, 97, ctx->internal_frames[0].paddr + ysize + ysize/4 + ysize/4); return 0; @@ -453,20 +484,21 @@ err: return ret; } -static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, +static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, int header_code, u8 *header, int *size) { + struct vb2_buffer *vb = &buf->vb2_buf; struct coda_dev *dev = ctx->dev; size_t bufsize; int ret; int i; if (dev->devtype->product == CODA_960) - memset(vb2_plane_vaddr(buf, 0), 0, 64); + memset(vb2_plane_vaddr(vb, 0), 0, 64); - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), + coda_write(dev, vb2_dma_contig_plane_dma_addr(vb, 0), CODA_CMD_ENC_HEADER_BB_START); - bufsize = vb2_plane_size(buf, 0); + bufsize = vb2_plane_size(vb, 0); if (dev->devtype->product == CODA_960) bufsize /= 1024; coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); @@ -479,14 +511,14 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, if (dev->devtype->product == CODA_960) { for (i = 63; i > 0; i--) - if (((char *)vb2_plane_vaddr(buf, 0))[i] != 0) + if (((char *)vb2_plane_vaddr(vb, 0))[i] != 0) break; *size = i + 1; } else { *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); } - memcpy(header, vb2_plane_vaddr(buf, 0), *size); + memcpy(header, vb2_plane_vaddr(vb, 0), *size); return 0; } @@ -712,6 +744,32 @@ err_clk_per: return ret; } +static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) +{ + u32 cache_size, cache_config; + + if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { + /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ + cache_size = 0x20262024; + cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + } else { + /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ + cache_size = 0x02440243; + cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; + } + coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); + if (fourcc == V4L2_PIX_FMT_NV12) { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } else { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } + coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); +} + /* * Encoder context operations */ @@ -743,7 +801,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct coda_q_data *q_data_src, *q_data_dst; u32 bitstream_buf, bitstream_size; - struct vb2_buffer *buf; + struct vb2_v4l2_buffer *buf; int gamma, ret, value; u32 dst_fourcc; int num_fb; @@ -754,7 +812,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) dst_fourcc = q_data_dst->fourcc; buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); + bitstream_buf = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); bitstream_size = q_data_dst->sizeimage; if (!coda_is_initialized(dev)) { @@ -789,9 +847,12 @@ static int coda_start_encoding(struct coda_ctx *ctx) break; } - ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE; + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); if (dev->devtype->product == CODA_DX6) { @@ -913,6 +974,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + value |= (ctx->params.vbv_delay & + CODA_RATECONTROL_INITIALDELAY_MASK) + << CODA_RATECONTROL_INITIALDELAY_OFFSET; if (dev->devtype->product == CODA_960) value |= BIT(31); /* disable autoskip */ } else { @@ -920,7 +984,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) } coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); + coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); coda_write(dev, ctx->params.intra_refresh, CODA_CMD_ENC_SEQ_INTRA_REFRESH); @@ -996,6 +1060,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) ret = -EFAULT; goto out; } + ctx->initialized = 1; if (dst_fourcc != V4L2_PIX_FMT_JPEG) { if (dev->devtype->product == CODA_960) @@ -1036,6 +1101,8 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.buf_btp_use, CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + coda9_set_frame_cache(ctx, q_data_src->fourcc); + /* FIXME */ coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A); @@ -1120,7 +1187,7 @@ out: static int coda_prepare_encode(struct coda_ctx *ctx) { struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct coda_dev *dev = ctx->dev; int force_ipicture; int quant_param = 0; @@ -1135,8 +1202,8 @@ static int coda_prepare_encode(struct coda_ctx *ctx) q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); dst_fourcc = q_data_dst->fourcc; - src_buf->v4l2_buf.sequence = ctx->osequence; - dst_buf->v4l2_buf.sequence = ctx->osequence; + src_buf->sequence = ctx->osequence; + dst_buf->sequence = ctx->osequence; ctx->osequence++; /* @@ -1144,12 +1211,12 @@ static int coda_prepare_encode(struct coda_ctx *ctx) * frame as IDR. This is a problem for some decoders that can't * recover when a frame is lost. */ - if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) { - src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + if (src_buf->sequence % ctx->params.gop_size) { + src_buf->flags |= V4L2_BUF_FLAG_PFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; } else { - src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; + src_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + src_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; } if (dev->devtype->product == CODA_960) @@ -1159,9 +1226,9 @@ static int coda_prepare_encode(struct coda_ctx *ctx) * Copy headers at the beginning of the first frame for H.264 only. * In MPEG4 they are already copied by the coda. */ - if (src_buf->v4l2_buf.sequence == 0) { + if (src_buf->sequence == 0) { pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0) + ctx->vpu_header_size[0] + ctx->vpu_header_size[1] + ctx->vpu_header_size[2]; @@ -1169,20 +1236,21 @@ static int coda_prepare_encode(struct coda_ctx *ctx) ctx->vpu_header_size[0] - ctx->vpu_header_size[1] - ctx->vpu_header_size[2]; - memcpy(vb2_plane_vaddr(dst_buf, 0), + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0), &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); - memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0], - &ctx->vpu_header[1][0], ctx->vpu_header_size[1]); - memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] + - ctx->vpu_header_size[1], &ctx->vpu_header[2][0], - ctx->vpu_header_size[2]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0], &ctx->vpu_header[1][0], + ctx->vpu_header_size[1]); + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_header_size[0] + ctx->vpu_header_size[1], + &ctx->vpu_header[2][0], ctx->vpu_header_size[2]); } else { pic_stream_buffer_addr = - vb2_dma_contig_plane_dma_addr(dst_buf, 0); + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); pic_stream_buffer_size = q_data_dst->sizeimage; } - if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { + if (src_buf->flags & V4L2_BUF_FLAG_KEYFRAME) { force_ipicture = 1; switch (dst_fourcc) { case V4L2_PIX_FMT_H264: @@ -1259,7 +1327,7 @@ static int coda_prepare_encode(struct coda_ctx *ctx) static void coda_finish_encode(struct coda_ctx *ctx) { - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct coda_dev *dev = ctx->dev; u32 wr_ptr, start_ptr; @@ -1273,13 +1341,13 @@ 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->v4l2_buf.sequence == 0) { - vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr + + if (dst_buf->sequence == 0) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, ctx->vpu_header_size[0] + ctx->vpu_header_size[1] + ctx->vpu_header_size[2]); } else { - vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr); } v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n", @@ -1289,23 +1357,23 @@ static void coda_finish_encode(struct coda_ctx *ctx) coda_read(dev, CODA_RET_ENC_PIC_FLAG); if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; } else { - dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + dst_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; } - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->v4l2_buf.flags |= - src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + dst_buf->timestamp = src_buf->timestamp; + dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->flags |= + src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->timecode = src_buf->timecode; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); ctx->gopcounter--; if (ctx->gopcounter < 0) @@ -1313,8 +1381,8 @@ static void coda_finish_encode(struct coda_ctx *ctx) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "job finished: encoding frame (%d) (%s)\n", - dst_buf->v4l2_buf.sequence, - (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? "KEYFRAME" : "PFRAME"); } @@ -1326,6 +1394,9 @@ static void coda_seq_end_work(struct work_struct *work) mutex_lock(&ctx->buffer_mutex); mutex_lock(&dev->coda_mutex); + if (ctx->initialized == 0) + goto out; + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__); @@ -1334,11 +1405,22 @@ static void coda_seq_end_work(struct work_struct *work) "CODA_COMMAND_SEQ_END failed\n"); } + /* + * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing + * from the output stream after the h.264 decoder has run. Resetting the + * hardware after the decoder has finished seems to help. + */ + if (dev->devtype->product == CODA_960) + coda_hw_reset(ctx); + kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); coda_free_framebuffers(ctx); + ctx->initialized = 0; + +out: mutex_unlock(&dev->coda_mutex); mutex_unlock(&ctx->buffer_mutex); } @@ -1448,9 +1530,12 @@ static int __coda_start_decoding(struct coda_ctx *ctx) /* Update coda bitstream read and write pointers from kfifo */ coda_kfifo_sync_to_device_full(ctx); - ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE; + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); if (dst_fourcc == V4L2_PIX_FMT_NV12) ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); ctx->display_idx = -1; @@ -1496,6 +1581,7 @@ static int __coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); return -ETIMEDOUT; } + ctx->initialized = 1; /* Update kfifo out pointer from coda bitstream read pointer */ coda_kfifo_sync_from_device(ctx); @@ -1578,30 +1664,13 @@ static int __coda_start_decoding(struct coda_ctx *ctx) CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); coda_write(dev, ctx->iram_info.buf_ovl_use, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) + if (dev->devtype->product == CODA_960) { coda_write(dev, ctx->iram_info.buf_btp_use, CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - } - if (dev->devtype->product == CODA_960) { - int cbb_size, crb_size; - - coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); - /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ - coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE); - - if (dst_fourcc == V4L2_PIX_FMT_NV12) { - cbb_size = 0; - crb_size = 16; - } else { - cbb_size = 8; - crb_size = 8; + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + coda9_set_frame_cache(ctx, dst_fourcc); } - coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET | - 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - cbb_size << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET | - crb_size << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET, - CODA9_CMD_SET_FRAME_CACHE_CONFIG); } if (src_fourcc == V4L2_PIX_FMT_H264) { @@ -1650,10 +1719,11 @@ static int coda_start_decoding(struct coda_ctx *ctx) static int coda_prepare_decode(struct coda_ctx *ctx) { - struct vb2_buffer *dst_buf; + struct vb2_v4l2_buffer *dst_buf; struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data_dst; struct coda_buffer_meta *meta; + unsigned long flags; u32 reg_addr, reg_stride; dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -1696,7 +1766,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) * well as the rotator buffer output. * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames. */ - coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index, + coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index, CODA9_CMD_DEC_PIC_ROT_INDEX); reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y; @@ -1732,6 +1802,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.axi_sram_use, CODA7_REG_BIT_AXI_SRAM_USE); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); meta = list_first_entry_or_null(&ctx->buffer_meta_list, struct coda_buffer_meta, list); @@ -1751,6 +1822,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) kfifo_in(&ctx->bitstream_fifo, buf, pad); } } + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); coda_kfifo_sync_to_device_full(ctx); @@ -1769,9 +1841,10 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data_src; struct coda_q_data *q_data_dst; - struct vb2_buffer *dst_buf; + struct vb2_v4l2_buffer *dst_buf; struct coda_buffer_meta *meta; unsigned long payload; + unsigned long flags; int width, height; int decoded_idx; int display_idx; @@ -1897,12 +1970,21 @@ static void coda_finish_decode(struct coda_ctx *ctx) } else { val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; val -= ctx->sequence_offset; - mutex_lock(&ctx->bitstream_mutex); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); if (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, list); list_del(&meta->list); - if (val != (meta->sequence & 0xffff)) { + ctx->num_metas--; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); + /* + * Clamp counters to 16 bits for comparison, as the HW + * counter rolls over at this point for h.264. This + * may be different for other formats, but using 16 bits + * should be enough to detect most errors and saves us + * from doing different things based on the format. + */ + if ((val & 0xffff) != (meta->sequence & 0xffff)) { v4l2_err(&dev->v4l2_dev, "sequence number mismatch (%d(%d) != %d)\n", val, ctx->sequence_offset, @@ -1911,13 +1993,13 @@ static void coda_finish_decode(struct coda_ctx *ctx) ctx->frame_metas[decoded_idx] = *meta; kfree(meta); } else { + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); memset(&ctx->frame_metas[decoded_idx], 0, sizeof(struct coda_buffer_meta)); ctx->frame_metas[decoded_idx].sequence = val; ctx->sequence_offset++; } - mutex_unlock(&ctx->bitstream_mutex); trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]); @@ -1950,17 +2032,17 @@ static void coda_finish_decode(struct coda_ctx *ctx) if (ctx->display_idx >= 0 && ctx->display_idx < ctx->num_internal_frames) { dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - dst_buf->v4l2_buf.sequence = ctx->osequence++; + dst_buf->sequence = ctx->osequence++; - dst_buf->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME); - dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx]; + dst_buf->flags |= ctx->frame_types[ctx->display_idx]; meta = &ctx->frame_metas[ctx->display_idx]; - dst_buf->v4l2_buf.timecode = meta->timecode; - dst_buf->v4l2_buf.timestamp = meta->timestamp; + dst_buf->timecode = meta->timecode; + dst_buf->timestamp = meta->timestamp; - trace_coda_dec_rot_done(ctx, meta, dst_buf); + trace_coda_dec_rot_done(ctx, dst_buf, meta); switch (q_data_dst->fourcc) { case V4L2_PIX_FMT_YUV420: @@ -1973,15 +2055,15 @@ static void coda_finish_decode(struct coda_ctx *ctx) payload = width * height * 2; break; } - vb2_set_plane_payload(dst_buf, 0, payload); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload); - v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ? + coda_m2m_buf_done(ctx, dst_buf, ctx->frame_errors[display_idx] ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "job finished: decoding frame (%d) (%s)\n", - dst_buf->v4l2_buf.sequence, - (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? + dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? "KEYFRAME" : "PFRAME"); } else { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, diff --git a/kernel/drivers/media/platform/coda/coda-common.c b/kernel/drivers/media/platform/coda/coda-common.c index 8e6fe0200..15516a6e3 100644 --- a/kernel/drivers/media/platform/coda/coda-common.c +++ b/kernel/drivers/media/platform/coda/coda-common.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -61,10 +62,9 @@ int coda_debug; module_param(coda_debug, int, 0644); MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); -struct coda_fmt { - char *name; - u32 fourcc; -}; +static int disable_tiling; +module_param(disable_tiling, int, 0644); +MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); void coda_write(struct coda_dev *dev, u32 data, u32 reg) { @@ -84,23 +84,23 @@ unsigned int coda_read(struct coda_dev *dev, u32 reg) } void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_buffer *buf, unsigned int reg_y) + struct vb2_v4l2_buffer *buf, unsigned int reg_y) { - u32 base_y = vb2_dma_contig_plane_dma_addr(buf, 0); + u32 base_y = vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, 0); u32 base_cb, base_cr; switch (q_data->fourcc) { - case V4L2_PIX_FMT_YVU420: - /* Switch Cb and Cr for YVU420 format */ - base_cr = base_y + q_data->bytesperline * q_data->height; - base_cb = base_cr + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: default: base_cb = base_y + q_data->bytesperline * q_data->height; base_cr = base_cb + q_data->bytesperline * q_data->height / 4; break; + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + base_cr = base_y + q_data->bytesperline * q_data->height; + base_cb = base_cr + q_data->bytesperline * q_data->height / 4; + break; case V4L2_PIX_FMT_YUV422P: base_cb = base_y + q_data->bytesperline * q_data->height; base_cr = base_cb + q_data->bytesperline * q_data->height / 2; @@ -111,40 +111,6 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, coda_write(ctx->dev, base_cr, reg_y + 8); } -/* - * Array of all formats supported by any version of Coda: - */ -static const struct coda_fmt coda_formats[] = { - { - .name = "YUV 4:2:0 Planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - }, - { - .name = "YUV 4:2:0 Planar, YCrCb", - .fourcc = V4L2_PIX_FMT_YVU420, - }, - { - .name = "YUV 4:2:0 Partial interleaved Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV12, - }, - { - .name = "YUV 4:2:2 Planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV422P, - }, - { - .name = "H264 Encoded Stream", - .fourcc = V4L2_PIX_FMT_H264, - }, - { - .name = "MPEG4 Encoded Stream", - .fourcc = V4L2_PIX_FMT_MPEG4, - }, - { - .name = "JPEG Encoded Images", - .fourcc = V4L2_PIX_FMT_JPEG, - }, -}; - #define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ { mode, src_fourcc, dst_fourcc, max_w, max_h } @@ -190,9 +156,9 @@ static const struct coda_video_device coda_bit_encoder = { .type = CODA_INST_ENCODER, .ops = &coda_bit_encode_ops, .src_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, }, .dst_formats = { V4L2_PIX_FMT_H264, @@ -205,9 +171,9 @@ static const struct coda_video_device coda_bit_jpeg_encoder = { .type = CODA_INST_ENCODER, .ops = &coda_bit_encode_ops, .src_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV422P, }, .dst_formats = { @@ -224,9 +190,9 @@ static const struct coda_video_device coda_bit_decoder = { V4L2_PIX_FMT_MPEG4, }, .dst_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, }, }; @@ -238,9 +204,9 @@ static const struct coda_video_device coda_bit_jpeg_decoder = { V4L2_PIX_FMT_JPEG, }, .dst_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV422P, }, }; @@ -261,38 +227,21 @@ static const struct coda_video_device *coda9_video_devices[] = { &coda_bit_decoder, }; -static bool coda_format_is_yuv(u32 fourcc) +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) { switch (fourcc) { + case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV422P: - return true; + return V4L2_PIX_FMT_YUV420; default: - return false; - } -} - -static const char *coda_format_name(u32 fourcc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(coda_formats); i++) { - if (coda_formats[i].fourcc == fourcc) - return coda_formats[i].name; + return fourcc; } - - return NULL; -} - -/* - * Normalize all supported YUV 4:2:0 formats to the value used in the codec - * tables. - */ -static u32 coda_format_normalize_yuv(u32 fourcc) -{ - return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc; } static const struct coda_codec *coda_find_codec(struct coda_dev *dev, @@ -396,7 +345,6 @@ static int coda_enum_fmt(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); const struct coda_video_device *cvd = to_coda_video_device(vdev); const u32 *formats; - const char *name; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) formats = cvd->src_formats; @@ -408,11 +356,7 @@ static int coda_enum_fmt(struct file *file, void *priv, if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) return -EINVAL; - name = coda_format_name(formats[f->index]); - strlcpy(f->description, name, sizeof(f->description)); f->pixelformat = formats[f->index]; - if (!coda_format_is_yuv(formats[f->index])) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; return 0; } @@ -504,9 +448,9 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, S_ALIGN); switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: /* * Frame stride must be at least multiple of 8, * but multiple of 16 for h.264 or JPEG 4:2:x @@ -645,6 +589,22 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) q_data->rect.width = f->fmt.pix.width; q_data->rect.height = f->fmt.pix.height; + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + if (!disable_tiling) + break; + } + /* else fall through */ + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; + break; + default: + break; + } + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Setting format for type %d, wxh: %dx%d, fmt: %d\n", f->type, q_data->width, q_data->height, q_data->fourcc); @@ -724,7 +684,7 @@ static int coda_qbuf(struct file *file, void *priv, } static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, - struct v4l2_buffer *buf) + struct vb2_v4l2_buffer *buf) { struct vb2_queue *src_vq; @@ -734,25 +694,20 @@ static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, (buf->sequence == (ctx->qsequence - 1))); } -static int coda_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) { - struct coda_ctx *ctx = fh_to_ctx(priv); - int ret; + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; - ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); - - /* If this is the last capture buffer, emit an end-of-stream event */ - if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - coda_buf_is_end_of_stream(ctx, buf)) { - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; + if (coda_buf_is_end_of_stream(ctx, buf)) { + buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_event_queue_fh(&ctx->fh, &eos_event); } - return ret; + v4l2_m2m_buf_done(buf, state); } static int coda_g_selection(struct file *file, void *fh, @@ -836,6 +791,104 @@ static int coda_decoder_cmd(struct file *file, void *fh, return 0; } +static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; + tpf->numerator = 1 + (ctx->params.framerate >> + CODA_FRATE_DIV_OFFSET); + + return 0; +} + +/* + * Approximate timeperframe v4l2_fract with values that can be written + * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. + */ +static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) +{ + struct v4l2_fract s = *timeperframe; + struct v4l2_fract f0; + struct v4l2_fract f1 = { 1, 0 }; + struct v4l2_fract f2 = { 0, 1 }; + unsigned int i, div, s_denominator; + + /* Lower bound is 1/65535 */ + if (s.numerator == 0 || s.denominator / s.numerator > 65535) { + timeperframe->numerator = 1; + timeperframe->denominator = 65535; + return; + } + + /* Upper bound is 65536/1, map everything above to infinity */ + if (s.denominator == 0 || s.numerator / s.denominator > 65536) { + timeperframe->numerator = 1; + timeperframe->denominator = 0; + return; + } + + /* Reduce fraction to lowest terms */ + div = gcd(s.numerator, s.denominator); + if (div > 1) { + s.numerator /= div; + s.denominator /= div; + } + + if (s.numerator <= 65536 && s.denominator < 65536) { + *timeperframe = s; + return; + } + + /* Find successive convergents from continued fraction expansion */ + while (f2.numerator <= 65536 && f2.denominator < 65536) { + f0 = f1; + f1 = f2; + + /* Stop when f2 exactly equals timeperframe */ + if (s.numerator == 0) + break; + + i = s.denominator / s.numerator; + + f2.numerator = f0.numerator + i * f1.numerator; + f2.denominator = f0.denominator + i * f2.denominator; + + s_denominator = s.numerator; + s.numerator = s.denominator % s.numerator; + s.denominator = s_denominator; + } + + *timeperframe = f1; +} + +static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) +{ + return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | + timeperframe->denominator; +} + +static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + tpf = &a->parm.output.timeperframe; + coda_approximate_timeperframe(tpf); + ctx->params.framerate = coda_timeperframe_to_frate(tpf); + + return 0; +} + static int coda_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { @@ -865,7 +918,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_qbuf = coda_qbuf, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_dqbuf = coda_dqbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_streamon = v4l2_m2m_ioctl_streamon, @@ -876,31 +929,13 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_try_decoder_cmd = coda_try_decoder_cmd, .vidioc_decoder_cmd = coda_decoder_cmd, + .vidioc_g_parm = coda_g_parm, + .vidioc_s_parm = coda_s_parm, + .vidioc_subscribe_event = coda_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -void coda_set_gdi_regs(struct coda_ctx *ctx) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - struct coda_dev *dev = ctx->dev; - int i; - - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ca_map[i], - CODA9_GDI_XY2_CAS_0 + 4 * i); - for (i = 0; i < 4; i++) - coda_write(dev, tiled_map->xy2ba_map[i], - CODA9_GDI_XY2_BA_0 + 4 * i); - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ra_map[i], - CODA9_GDI_XY2_RAS_0 + 4 * i); - coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); - for (i = 0; i < 32; i++) - coda_write(dev, tiled_map->rbc2axi_map[i], - CODA9_GDI_RBC2_AXI_0 + 4 * i); -} - /* * Mem-to-mem operations. */ @@ -954,14 +989,14 @@ static void coda_pic_run_work(struct work_struct *work) static int coda_job_ready(void *m2m_priv) { struct coda_ctx *ctx = m2m_priv; + int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); /* * For both 'P' and 'key' frame cases 1 picture * and 1 frame are needed. In the decoder case, * the compressed frame can be in the bitstream. */ - if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - ctx->inst_type != CODA_INST_DECODER) { + if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: not enough video buffers.\n"); return 0; @@ -974,27 +1009,17 @@ static int coda_job_ready(void *m2m_priv) } if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - struct list_head *meta; - bool stream_end; - int num_metas; - int src_bufs; + bool stream_end = ctx->bit_stream_param & + CODA_BIT_STREAM_END_FLAG; + int num_metas = ctx->num_metas; - if (ctx->hold && !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + if (ctx->hold && !src_bufs) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: on hold for more buffers.\n", ctx->idx); return 0; } - stream_end = ctx->bit_stream_param & - CODA_BIT_STREAM_END_FLAG; - - num_metas = 0; - list_for_each(meta, &ctx->buffer_meta_list) - num_metas++; - - src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); - if (!stream_end && (num_metas + src_bufs) < 2) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: need 2 buffers available (%d, %d)\n", @@ -1003,8 +1028,8 @@ static int coda_job_ready(void *m2m_priv) } - if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - !stream_end && (coda_get_bitstream_payload(ctx) < 512)) { + if (!src_bufs && !stream_end && + (coda_get_bitstream_payload(ctx) < 512)) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: not enough bitstream data (%d).\n", ctx->idx, coda_get_bitstream_payload(ctx)); @@ -1020,6 +1045,7 @@ static int coda_job_ready(void *m2m_priv) v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "job ready\n"); + return 1; } @@ -1057,32 +1083,6 @@ static const struct v4l2_m2m_ops coda_m2m_ops = { .unlock = coda_unlock, }; -static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - int luma_map, chro_map, i; - - memset(tiled_map, 0, sizeof(*tiled_map)); - - luma_map = 64; - chro_map = 64; - tiled_map->map_type = tiled_map_type; - for (i = 0; i < 16; i++) - tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 4; i++) - tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 16; i++) - tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map; - - if (tiled_map_type == GDI_LINEAR_FRAME_MAP) { - tiled_map->xy2rbc_config = 0; - } else { - dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n", - tiled_map_type); - return; - } -} - static void set_default_params(struct coda_ctx *ctx) { unsigned int max_w, max_h, usize, csize; @@ -1099,8 +1099,8 @@ static void set_default_params(struct coda_ctx *ctx) ctx->params.framerate = 30; /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc; - ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; ctx->q_data[V4L2_M2M_SRC].width = max_w; ctx->q_data[V4L2_M2M_SRC].height = max_h; ctx->q_data[V4L2_M2M_DST].width = max_w; @@ -1121,15 +1121,17 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].rect.width = max_w; ctx->q_data[V4L2_M2M_DST].rect.height = max_h; - if (ctx->dev->devtype->product == CODA_960) - coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); + /* + * Since the RBC2AXI logic only supports a single chroma plane, + * macroblock tiling only works for to NV12 pixel format. + */ + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; } /* * Queue operations */ -static int coda_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, +static int coda_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -1172,6 +1174,7 @@ static int coda_buf_prepare(struct vb2_buffer *vb) static void coda_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vb2_queue *vq = vb->vb2_queue; struct coda_q_data *q_data; @@ -1190,12 +1193,12 @@ static void coda_buf_queue(struct vb2_buffer *vb) if (vb2_get_plane_payload(vb, 0) == 0) coda_bit_stream_end_flag(ctx); mutex_lock(&ctx->bitstream_mutex); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); if (vb2_is_streaming(vb->vb2_queue)) coda_fill_bitstream(ctx, true); mutex_unlock(&ctx->bitstream_mutex); } else { - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } } @@ -1244,14 +1247,12 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) struct coda_ctx *ctx = vb2_get_drv_priv(q); struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; struct coda_q_data *q_data_src, *q_data_dst; - struct vb2_buffer *buf; + struct vb2_v4l2_buffer *buf; int ret = 0; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (q_data_src->fourcc == V4L2_PIX_FMT_H264 || - (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - ctx->dev->devtype->product == CODA_7541)) { + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { /* copy the buffers that were queued before streamon */ mutex_lock(&ctx->bitstream_mutex); coda_fill_bitstream(ctx, false); @@ -1320,7 +1321,6 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto err; } - ctx->initialized = 1; return ret; err: @@ -1338,7 +1338,8 @@ static void coda_stop_streaming(struct vb2_queue *q) { struct coda_ctx *ctx = vb2_get_drv_priv(q); struct coda_dev *dev = ctx->dev; - struct vb2_buffer *buf; + struct vb2_v4l2_buffer *buf; + unsigned long flags; bool stop; stop = ctx->streamon_out && ctx->streamon_cap; @@ -1373,20 +1374,23 @@ static void coda_stop_streaming(struct vb2_queue *q) queue_work(dev->workqueue, &ctx->seq_end_work); flush_work(&ctx->seq_end_work); } - mutex_lock(&ctx->bitstream_mutex); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); while (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, list); list_del(&meta->list); kfree(meta); } - mutex_unlock(&ctx->bitstream_mutex); + ctx->num_metas = 0; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); - ctx->initialized = 0; ctx->runcounter = 0; ctx->aborting = 0; } + + if (!ctx->streamon_out && !ctx->streamon_cap) + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; } static const struct vb2_ops coda_qops = { @@ -1474,6 +1478,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_JPEG_RESTART_INTERVAL: ctx->params.jpeg_restart_interval = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VBV_DELAY: + ctx->params.vbv_delay = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); + break; default: v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", @@ -1533,6 +1543,14 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); + /* + * The maximum VBV size value is 0x7fffffff bits, + * one bit less than 262144 KiB + */ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); } static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) @@ -1731,6 +1749,7 @@ static int coda_open(struct file *file) mutex_init(&ctx->bitstream_mutex); mutex_init(&ctx->buffer_mutex); INIT_LIST_HEAD(&ctx->buffer_meta_list); + spin_lock_init(&ctx->buffer_meta_lock); coda_lock(ctx); list_add(&ctx->list, &dev->instances); @@ -1774,7 +1793,7 @@ static int coda_release(struct file *file) v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->initialized && ctx->ops->seq_end_work) { + if (ctx->ops->seq_end_work) { queue_work(dev->workqueue, &ctx->seq_end_work); flush_work(&ctx->seq_end_work); } @@ -2160,9 +2179,9 @@ static int coda_probe(struct platform_device *pdev) } /* Get IRAM pool from device tree or platform data */ - pool = of_get_named_gen_pool(np, "iram", 0); + pool = of_gen_pool_get(np, "iram", 0); if (!pool && pdata) - pool = dev_get_gen_pool(pdata->iram_dev); + pool = gen_pool_get(pdata->iram_dev, NULL); if (!pool) { dev_err(&pdev->dev, "iram pool not available\n"); return -ENOMEM; diff --git a/kernel/drivers/media/platform/coda/coda-gdi.c b/kernel/drivers/media/platform/coda/coda-gdi.c new file mode 100644 index 000000000..aaa7afc68 --- /dev/null +++ b/kernel/drivers/media/platform/coda/coda-gdi.c @@ -0,0 +1,150 @@ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include "coda.h" + +#define XY2_INVERT BIT(7) +#define XY2_ZERO BIT(6) +#define XY2_TB_XOR BIT(5) +#define XY2_XYSEL BIT(4) +#define XY2_Y (1 << 4) +#define XY2_X (0 << 4) + +#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((XY2_##luma_sel) | (luma_bit)) << 8 | \ + (XY2_##chroma_sel) | (chroma_bit)) + +static const u16 xy2ca_zero_map[16] = { + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +static const u16 xy2ca_tiled_map[16] = { + XY2(Y, 0, Y, 0), + XY2(Y, 1, Y, 1), + XY2(Y, 2, Y, 2), + XY2(Y, 3, X, 3), + XY2(X, 3, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +/* + * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock + * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). + * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. + */ + +#define RBC_CA (0 << 4) +#define RBC_BA (1 << 4) +#define RBC_RA (2 << 4) +#define RBC_ZERO (3 << 4) + +#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((RBC_##luma_sel) | (luma_bit)) << 6 | \ + (RBC_##chroma_sel) | (chroma_bit)) + +static const u16 rbc2axi_tiled_map[32] = { + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(CA, 0, CA, 0), + RBC(CA, 1, CA, 1), + RBC(CA, 2, CA, 2), + RBC(CA, 3, CA, 3), + RBC(CA, 4, CA, 8), + RBC(CA, 8, CA, 9), + RBC(CA, 9, CA, 10), + RBC(CA, 10, CA, 11), + RBC(CA, 11, CA, 12), + RBC(CA, 12, CA, 13), + RBC(CA, 13, CA, 14), + RBC(CA, 14, CA, 15), + RBC(CA, 15, RA, 0), + RBC(RA, 0, RA, 1), + RBC(RA, 1, RA, 2), + RBC(RA, 2, RA, 3), + RBC(RA, 3, RA, 4), + RBC(RA, 4, RA, 5), + RBC(RA, 5, RA, 6), + RBC(RA, 6, RA, 7), + RBC(RA, 7, RA, 8), + RBC(RA, 8, RA, 9), + RBC(RA, 9, RA, 10), + RBC(RA, 10, RA, 11), + RBC(RA, 11, RA, 12), + RBC(RA, 12, RA, 13), + RBC(RA, 13, RA, 14), + RBC(RA, 14, RA, 15), + RBC(RA, 15, ZERO, 0), +}; + +void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + const u16 *xy2ca_map; + u32 xy2rbc_config; + int i; + + switch (ctx->tiled_map_type) { + case GDI_LINEAR_FRAME_MAP: + default: + xy2ca_map = xy2ca_zero_map; + xy2rbc_config = 0; + break; + case GDI_TILED_FRAME_MB_RASTER_MAP: + xy2ca_map = xy2ca_tiled_map; + xy2rbc_config = CODA9_XY2RBC_TILED_MAP | + CODA9_XY2RBC_CA_INC_HOR | + (16 - 1) << 12 | (8 - 1) << 4; + break; + } + + for (i = 0; i < 16; i++) + coda_write(dev, xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + if (xy2rbc_config) { + for (i = 0; i < 32; i++) + coda_write(dev, rbc2axi_tiled_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); + } +} diff --git a/kernel/drivers/media/platform/coda/coda-jpeg.c b/kernel/drivers/media/platform/coda/coda-jpeg.c index 11e734bc2..96cd42a0b 100644 --- a/kernel/drivers/media/platform/coda/coda-jpeg.c +++ b/kernel/drivers/media/platform/coda/coda-jpeg.c @@ -178,12 +178,12 @@ int coda_jpeg_write_tables(struct coda_ctx *ctx) return 0; } -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb) +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_v4l2_buffer *vb) { - void *vaddr = vb2_plane_vaddr(vb, 0); + void *vaddr = vb2_plane_vaddr(&vb->vb2_buf, 0); u16 soi = be16_to_cpup((__be16 *)vaddr); u16 eoi = be16_to_cpup((__be16 *)(vaddr + - vb2_get_plane_payload(vb, 0) - 2)); + vb2_get_plane_payload(&vb->vb2_buf, 0) - 2)); return soi == SOI_MARKER && eoi == EOI_MARKER; } diff --git a/kernel/drivers/media/platform/coda/coda.h b/kernel/drivers/media/platform/coda/coda.h index 6a5c8f6c6..96532b06b 100644 --- a/kernel/drivers/media/platform/coda/coda.h +++ b/kernel/drivers/media/platform/coda/coda.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "coda_regs.h" @@ -128,6 +128,8 @@ struct coda_params { enum v4l2_mpeg_video_multi_slice_mode slice_mode; u32 framerate; u16 bitrate; + u16 vbv_delay; + u32 vbv_size; u32 slice_max_bits; u32 slice_max_mb; }; @@ -165,15 +167,8 @@ struct coda_iram_info { phys_addr_t next_paddr; }; -struct gdi_tiled_map { - int xy2ca_map[16]; - int xy2ba_map[16]; - int xy2ra_map[16]; - int rbc2axi_map[32]; - int xy2rbc_config; - int map_type; #define GDI_LINEAR_FRAME_MAP 0 -}; +#define GDI_TILED_FRAME_MB_RASTER_MAP 1 struct coda_ctx; @@ -227,12 +222,14 @@ struct coda_ctx { struct coda_buffer_meta frame_metas[CODA_MAX_FRAMEBUFFERS]; u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; struct list_head buffer_meta_list; + spinlock_t buffer_meta_lock; + int num_metas; struct coda_aux_buf workbuf; int num_internal_frames; int idx; int reg_idx; struct coda_iram_info iram_info; - struct gdi_tiled_map tiled_map; + int tiled_map_type; u32 bit_stream_param; u32 frm_dis_flg; u32 frame_mem_ctrl; @@ -246,7 +243,7 @@ extern int coda_debug; void coda_write(struct coda_dev *dev, u32 data, u32 reg); unsigned int coda_read(struct coda_dev *dev, u32 reg); void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, - struct vb2_buffer *buf, unsigned int reg_y); + struct vb2_v4l2_buffer *buf, unsigned int reg_y); int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, size_t size, const char *name, struct dentry *parent); @@ -287,9 +284,12 @@ static inline unsigned int coda_get_bitstream_payload(struct coda_ctx *ctx) void coda_bit_stream_end_flag(struct coda_ctx *ctx); +void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state); + int coda_h264_padding(int size, char *p); -bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); +bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_v4l2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); diff --git a/kernel/drivers/media/platform/coda/coda_regs.h b/kernel/drivers/media/platform/coda/coda_regs.h index 7d0262411..3490602fa 100644 --- a/kernel/drivers/media/platform/coda/coda_regs.h +++ b/kernel/drivers/media/platform/coda/coda_regs.h @@ -51,6 +51,7 @@ #define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) #define CODA_STREAM_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 +#define CODA9_FRAME_TILED2LINEAR (1 << 11) #define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) #define CODA_IMAGE_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 @@ -263,6 +264,10 @@ #define CODADX6_PICHEIGHT_MASK 0x3ff #define CODA7_PICHEIGHT_MASK 0xffff #define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 +#define CODA_FRATE_RES_OFFSET 0 +#define CODA_FRATE_RES_MASK 0xffff +#define CODA_FRATE_DIV_OFFSET 16 +#define CODA_FRATE_DIV_MASK 0xffff #define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 #define CODA_MP4PARAM_VERID_OFFSET 6 #define CODA_MP4PARAM_VERID_MASK 0x01 @@ -448,7 +453,12 @@ #define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) #define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) +#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) +#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) +#define CODA9_XY2RBC_TILED_MAP BIT(17) +#define CODA9_XY2RBC_CA_INC_HOR BIT(16) #define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) #define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) +#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) #endif diff --git a/kernel/drivers/media/platform/coda/trace.h b/kernel/drivers/media/platform/coda/trace.h index d1d06cbd1..f20666a4a 100644 --- a/kernel/drivers/media/platform/coda/trace.h +++ b/kernel/drivers/media/platform/coda/trace.h @@ -5,12 +5,10 @@ #define __CODA_TRACE_H__ #include -#include +#include #include "coda.h" -#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) - TRACE_EVENT(coda_bit_run, TP_PROTO(struct coda_ctx *ctx, int cmd), @@ -50,8 +48,8 @@ TRACE_EVENT(coda_bit_done, TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) ); -TRACE_EVENT(coda_enc_pic_run, - TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), +DECLARE_EVENT_CLASS(coda_buf_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), TP_ARGS(ctx, buf), @@ -63,7 +61,7 @@ TRACE_EVENT(coda_enc_pic_run, TP_fast_assign( __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->v4l2_buf.index; + __entry->index = buf->vb2_buf.index; __entry->ctx = ctx->idx; ), @@ -71,29 +69,18 @@ TRACE_EVENT(coda_enc_pic_run, __entry->minor, __entry->index, __entry->ctx) ); -TRACE_EVENT(coda_enc_pic_done, - TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), - - TP_ARGS(ctx, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->v4l2_buf.index; - __entry->ctx = ctx->idx; - ), +DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) +); - TP_printk("minor = %d, index = %d, ctx = %d", - __entry->minor, __entry->index, __entry->ctx) +DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf), + TP_ARGS(ctx, buf) ); -TRACE_EVENT(coda_bit_queue, - TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf, +DECLARE_EVENT_CLASS(coda_buf_meta_class, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, struct coda_buffer_meta *meta), TP_ARGS(ctx, buf, meta), @@ -108,7 +95,7 @@ TRACE_EVENT(coda_bit_queue, TP_fast_assign( __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->v4l2_buf.index; + __entry->index = buf->vb2_buf.index; __entry->start = meta->start; __entry->end = meta->end; __entry->ctx = ctx->idx; @@ -119,7 +106,13 @@ TRACE_EVENT(coda_bit_queue, __entry->ctx) ); -TRACE_EVENT(coda_dec_pic_run, +DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DECLARE_EVENT_CLASS(coda_meta_class, TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), TP_ARGS(ctx, meta), @@ -142,54 +135,20 @@ TRACE_EVENT(coda_dec_pic_run, __entry->minor, __entry->start, __entry->end, __entry->ctx) ); -TRACE_EVENT(coda_dec_pic_done, +DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - - TP_ARGS(ctx, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta->start; - __entry->end = meta->end; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->ctx) + TP_ARGS(ctx, meta) ); -TRACE_EVENT(coda_dec_rot_done, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta, - struct vb2_buffer *buf), - - TP_ARGS(ctx, meta, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta->start; - __entry->end = meta->end; - __entry->index = buf->v4l2_buf.index; - __entry->ctx = ctx->idx; - ), +DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); - TP_printk("minor = %d, start = 0x%x, end = 0x%x, index = %d, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->index, - __entry->ctx) +DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) ); #endif /* __CODA_TRACE_H__ */ diff --git a/kernel/drivers/media/platform/davinci/vpbe_display.c b/kernel/drivers/media/platform/davinci/vpbe_display.c index c4ab46f5b..6d91422c4 100644 --- a/kernel/drivers/media/platform/davinci/vpbe_display.c +++ b/kernel/drivers/media/platform/davinci/vpbe_display.c @@ -71,16 +71,11 @@ static int venc_is_second_field(struct vpbe_display *disp_dev) static void vpbe_isr_even_field(struct vpbe_display *disp_obj, struct vpbe_layer *layer) { - struct timespec timevalue; - if (layer->cur_frm == layer->next_frm) return; - ktime_get_ts(&timevalue); - layer->cur_frm->vb.v4l2_buf.timestamp.tv_sec = - timevalue.tv_sec; - layer->cur_frm->vb.v4l2_buf.timestamp.tv_usec = - timevalue.tv_nsec / NSEC_PER_USEC; - vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_DONE); + + v4l2_get_timestamp(&layer->cur_frm->vb.timestamp); + vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ layer->cur_frm = layer->next_frm; } @@ -109,8 +104,8 @@ static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, list_del(&layer->next_frm->list); spin_unlock(&disp_obj->dma_queue_lock); /* Mark state of the frame to active */ - layer->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; - addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb, 0); + layer->next_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb.vb2_buf, 0); osd_device->ops.start_layer(osd_device, layer->layer_info.id, addr, @@ -233,11 +228,12 @@ static int vpbe_buffer_prepare(struct vb2_buffer *vb) * This function allocates memory for the buffers */ static int -vpbe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +vpbe_buffer_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; /* Get the file handle object and layer object */ struct vpbe_layer *layer = vb2_get_drv_priv(vq); struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; @@ -264,8 +260,9 @@ vpbe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, */ static void vpbe_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); /* Get the file handle object and layer object */ - struct vpbe_disp_buffer *buf = container_of(vb, + struct vpbe_disp_buffer *buf = container_of(vbuf, struct vpbe_disp_buffer, vb); struct vpbe_layer *layer = vb2_get_drv_priv(vb->vb2_queue); struct vpbe_display *disp = layer->disp_dev; @@ -295,7 +292,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) /* Remove buffer from the buffer queue */ list_del(&layer->cur_frm->list); /* Mark state of the current frame to active */ - layer->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + layer->cur_frm->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; /* Initialize field_id and started member */ layer->field_id = 0; @@ -304,10 +301,12 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret < 0) { struct vpbe_disp_buffer *buf, *tmp; - vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } return ret; @@ -337,13 +336,14 @@ static void vpbe_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ spin_lock_irqsave(&disp->dma_queue_lock, flags); if (layer->cur_frm == layer->next_frm) { - vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } else { if (layer->cur_frm != NULL) - vb2_buffer_done(&layer->cur_frm->vb, + vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); if (layer->next_frm != NULL) - vb2_buffer_done(&layer->next_frm->vb, + vb2_buffer_done(&layer->next_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); } @@ -351,7 +351,8 @@ static void vpbe_stop_streaming(struct vb2_queue *vq) layer->next_frm = list_entry(layer->dma_queue.next, struct vpbe_disp_buffer, list); list_del(&layer->next_frm->list); - vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&layer->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&disp->dma_queue_lock, flags); } @@ -388,7 +389,7 @@ static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, unsigned long addr; int ret; - addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb.vb2_buf, 0); /* Set address in the display registers */ osd_device->ops.start_layer(osd_device, layer->layer_info.id, diff --git a/kernel/drivers/media/platform/davinci/vpfe_capture.c b/kernel/drivers/media/platform/davinci/vpfe_capture.c index ccfcf3f52..7767e072d 100644 --- a/kernel/drivers/media/platform/davinci/vpfe_capture.c +++ b/kernel/drivers/media/platform/davinci/vpfe_capture.c @@ -370,7 +370,7 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) * For a given standard, this functions sets up the default * pix format & crop values in the vpfe device and ccdc. It first * starts with defaults based values from the standard table. - * It then checks if sub device support g_mbus_fmt and then override the + * It then checks if sub device supports get_fmt and then override the * values based on that.Sets crop values to match with scan resolution * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the * values in ccdc @@ -379,7 +379,10 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, v4l2_std_id std_id) { struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix; int i, ret = 0; @@ -413,26 +416,26 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, pix->field = V4L2_FIELD_INTERLACED; /* assume V4L2_PIX_FMT_UYVY as default */ pix->pixelformat = V4L2_PIX_FMT_UYVY; - v4l2_fill_mbus_format(&mbus_fmt, pix, + v4l2_fill_mbus_format(mbus_fmt, pix, MEDIA_BUS_FMT_YUYV10_2X10); } else { pix->field = V4L2_FIELD_NONE; /* assume V4L2_PIX_FMT_SBGGR8 */ pix->pixelformat = V4L2_PIX_FMT_SBGGR8; - v4l2_fill_mbus_format(&mbus_fmt, pix, + v4l2_fill_mbus_format(mbus_fmt, pix, MEDIA_BUS_FMT_SBGGR8_1X8); } - /* if sub device supports g_mbus_fmt, override the defaults */ + /* if sub device supports get_fmt, override the defaults */ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, - sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt); + sdinfo->grp_id, pad, get_fmt, NULL, &fmt); if (ret && ret != -ENOIOCTLCMD) { v4l2_err(&vpfe_dev->v4l2_dev, - "error in getting g_mbus_fmt from sub device\n"); + "error in getting get_fmt from sub device\n"); return ret; } - v4l2_fill_pix_format(pix, &mbus_fmt); + v4l2_fill_pix_format(pix, mbus_fmt); pix->bytesperline = pix->width * 2; pix->sizeimage = pix->bytesperline * pix->height; diff --git a/kernel/drivers/media/platform/davinci/vpif_capture.c b/kernel/drivers/media/platform/davinci/vpif_capture.c index a5f548138..c1e573b7c 100644 --- a/kernel/drivers/media/platform/davinci/vpif_capture.c +++ b/kernel/drivers/media/platform/davinci/vpif_capture.c @@ -57,7 +57,8 @@ static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = { {1, 1} }; /* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */ static int ycmux_mode; -static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb) +static inline +struct vpif_cap_buffer *to_vpif_buffer(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct vpif_cap_buffer, vb); } @@ -72,6 +73,7 @@ static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb) */ static int vpif_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *q = vb->vb2_queue; struct channel_obj *ch = vb2_get_drv_priv(q); struct common_obj *common; @@ -85,7 +87,7 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) return -EINVAL; - vb->v4l2_buf.field = common->fmt.fmt.pix.field; + vbuf->field = common->fmt.fmt.pix.field; addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (!IS_ALIGNED((addr + common->ytop_off), 8) || @@ -112,10 +114,11 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common; @@ -145,8 +148,9 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, */ static void vpif_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); - struct vpif_cap_buffer *buf = to_vpif_buffer(vb); + struct vpif_cap_buffer *buf = to_vpif_buffer(vbuf); struct common_obj *common; unsigned long flags; @@ -214,7 +218,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) list_del(&common->cur_frm->list); spin_unlock_irqrestore(&common->irqlock, flags); - addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb.vb2_buf, 0); common->set_addr(addr + common->ytop_off, addr + common->ybtm_off, @@ -243,7 +247,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) err: list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } spin_unlock_irqrestore(&common->irqlock, flags); @@ -286,13 +290,14 @@ static void vpif_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); if (common->cur_frm == common->next_frm) { - vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } else { if (common->cur_frm != NULL) - vb2_buffer_done(&common->cur_frm->vb, + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); if (common->next_frm != NULL) - vb2_buffer_done(&common->next_frm->vb, + vb2_buffer_done(&common->next_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); } @@ -300,7 +305,8 @@ static void vpif_stop_streaming(struct vb2_queue *vq) common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); list_del(&common->next_frm->list); - vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&common->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); } @@ -325,9 +331,8 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&common->cur_frm->vb.timestamp); + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */ common->cur_frm = common->next_frm; } @@ -350,7 +355,7 @@ static void vpif_schedule_next_buffer(struct common_obj *common) /* Remove that buffer from the buffer queue */ list_del(&common->next_frm->list); spin_unlock(&common->irqlock); - addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb.vb2_buf, 0); /* Set top and bottom field addresses in VPIF registers */ common->set_addr(addr + common->ytop_off, diff --git a/kernel/drivers/media/platform/davinci/vpif_capture.h b/kernel/drivers/media/platform/davinci/vpif_capture.h index 8b8a663f6..4a7600929 100644 --- a/kernel/drivers/media/platform/davinci/vpif_capture.h +++ b/kernel/drivers/media/platform/davinci/vpif_capture.h @@ -52,7 +52,7 @@ struct video_obj { }; struct vpif_cap_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; diff --git a/kernel/drivers/media/platform/davinci/vpif_display.c b/kernel/drivers/media/platform/davinci/vpif_display.c index 682e5d578..fd2780306 100644 --- a/kernel/drivers/media/platform/davinci/vpif_display.c +++ b/kernel/drivers/media/platform/davinci/vpif_display.c @@ -53,7 +53,8 @@ static struct device *vpif_dev; static void vpif_calculate_offsets(struct channel_obj *ch); static void vpif_config_addr(struct channel_obj *ch, int muxmode); -static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb) +static inline +struct vpif_disp_buffer *to_vpif_buffer(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct vpif_disp_buffer, vb); } @@ -68,6 +69,7 @@ static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb) */ static int vpif_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); struct common_obj *common; @@ -77,7 +79,7 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) return -EINVAL; - vb->v4l2_buf.field = common->fmt.fmt.pix.field; + vbuf->field = common->fmt.fmt.pix.field; if (vb->vb2_queue->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { unsigned long addr = vb2_dma_contig_plane_dma_addr(vb, 0); @@ -107,10 +109,11 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -138,7 +141,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, */ static void vpif_buffer_queue(struct vb2_buffer *vb) { - struct vpif_disp_buffer *buf = to_vpif_buffer(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpif_disp_buffer *buf = to_vpif_buffer(vbuf); struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); struct common_obj *common; unsigned long flags; @@ -197,7 +201,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) list_del(&common->cur_frm->list); spin_unlock_irqrestore(&common->irqlock, flags); - addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb.vb2_buf, 0); common->set_addr((addr + common->ytop_off), (addr + common->ybtm_off), (addr + common->ctop_off), @@ -229,7 +233,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) err: list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } spin_unlock_irqrestore(&common->irqlock, flags); @@ -264,13 +268,14 @@ static void vpif_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); if (common->cur_frm == common->next_frm) { - vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } else { if (common->cur_frm != NULL) - vb2_buffer_done(&common->cur_frm->vb, + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); if (common->next_frm != NULL) - vb2_buffer_done(&common->next_frm->vb, + vb2_buffer_done(&common->next_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); } @@ -278,7 +283,8 @@ static void vpif_stop_streaming(struct vb2_queue *vq) common->next_frm = list_entry(common->dma_queue.next, struct vpif_disp_buffer, list); list_del(&common->next_frm->list); - vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&common->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); } @@ -306,7 +312,7 @@ static void process_progressive_mode(struct common_obj *common) spin_unlock(&common->irqlock); /* Set top and bottom field addrs in VPIF registers */ - addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb.vb2_buf, 0); common->set_addr(addr + common->ytop_off, addr + common->ybtm_off, addr + common->ctop_off, @@ -324,10 +330,10 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.timestamp); /* Change status of the cur_frm */ - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ common->cur_frm = common->next_frm; @@ -380,10 +386,10 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - v4l2_get_timestamp(&common->cur_frm->vb. - v4l2_buf.timestamp); - vb2_buffer_done(&common->cur_frm->vb, - VB2_BUF_STATE_DONE); + v4l2_get_timestamp( + &common->cur_frm->vb.timestamp); + vb2_buffer_done(&common->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ common->cur_frm = common->next_frm; } diff --git a/kernel/drivers/media/platform/davinci/vpif_display.h b/kernel/drivers/media/platform/davinci/vpif_display.h index 849e0e385..e7a1723a1 100644 --- a/kernel/drivers/media/platform/davinci/vpif_display.h +++ b/kernel/drivers/media/platform/davinci/vpif_display.h @@ -62,7 +62,7 @@ struct video_obj { }; struct vpif_disp_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; diff --git a/kernel/drivers/media/platform/exynos-gsc/gsc-core.c b/kernel/drivers/media/platform/exynos-gsc/gsc-core.c index fd2891c88..9b9e423e4 100644 --- a/kernel/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/kernel/drivers/media/platform/exynos-gsc/gsc-core.c @@ -967,7 +967,7 @@ static struct gsc_driverdata gsc_v_100_drvdata = { .lclk_frequency = 266000000UL, }; -static struct platform_device_id gsc_driver_ids[] = { +static const struct platform_device_id gsc_driver_ids[] = { { .name = "exynos-gsc", .driver_data = (unsigned long)&gsc_v_100_drvdata, diff --git a/kernel/drivers/media/platform/exynos-gsc/gsc-core.h b/kernel/drivers/media/platform/exynos-gsc/gsc-core.h index fa572aacd..e93a2336c 100644 --- a/kernel/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/kernel/drivers/media/platform/exynos-gsc/gsc-core.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -136,7 +136,7 @@ struct gsc_fmt { * @idx : index of G-Scaler input buffer */ struct gsc_input_buf { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; int idx; }; diff --git a/kernel/drivers/media/platform/exynos-gsc/gsc-m2m.c b/kernel/drivers/media/platform/exynos-gsc/gsc-m2m.c index d5cffef2e..d82e717ac 100644 --- a/kernel/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/kernel/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -77,7 +77,7 @@ static void gsc_m2m_stop_streaming(struct vb2_queue *q) void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) { - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; if (!ctx || !ctx->m2m_ctx) return; @@ -86,11 +86,11 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (src_vb && dst_vb) { - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; - dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode; - dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.flags |= - src_vb->v4l2_buf.flags + dst_vb->timestamp = src_vb->timestamp; + dst_vb->timecode = src_vb->timecode; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; v4l2_m2m_buf_done(src_vb, vb_state); @@ -109,23 +109,23 @@ static void gsc_m2m_job_abort(void *priv) static int gsc_get_bufs(struct gsc_ctx *ctx) { struct gsc_frame *s_frame, *d_frame; - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; int ret; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); + ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr); if (ret) return ret; dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); + ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr); if (ret) return ret; - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + dst_vb->timestamp = src_vb->timestamp; return 0; } @@ -212,7 +212,7 @@ put_device: } static int gsc_m2m_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { @@ -255,12 +255,13 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) static void gsc_m2m_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } static struct vb2_ops gsc_m2m_qops = { diff --git a/kernel/drivers/media/platform/exynos4-is/Kconfig b/kernel/drivers/media/platform/exynos4-is/Kconfig index b7b2e4722..40423c6c5 100644 --- a/kernel/drivers/media/platform/exynos4-is/Kconfig +++ b/kernel/drivers/media/platform/exynos4-is/Kconfig @@ -57,6 +57,7 @@ endif config VIDEO_EXYNOS4_FIMC_IS tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" + depends on I2C depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG depends on OF diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-capture.c b/kernel/drivers/media/platform/exynos4-is/fimc-capture.c index cfebf292e..99e57320e 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/kernel/drivers/media/platform/exynos4-is/fimc-capture.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "common.h" @@ -103,7 +103,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) /* Release unused buffers */ while (!suspend && !list_empty(&cap->pending_buf_q)) { buf = fimc_pending_queue_pop(cap); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } /* If suspending put unused buffers onto pending queue */ while (!list_empty(&cap->active_buf_q)) { @@ -111,7 +111,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) if (suspend) fimc_pending_queue_add(cap, buf); else - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } fimc_hw_reset(fimc); @@ -183,8 +183,6 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) struct v4l2_subdev *csis = p->subdevs[IDX_CSIS]; struct fimc_frame *f = &cap->ctx->d_frame; struct fimc_vid_buffer *v_buf; - struct timeval *tv; - struct timespec ts; if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { wake_up(&fimc->irq_queue); @@ -193,16 +191,12 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) if (!list_empty(&cap->active_buf_q) && test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) { - ktime_get_real_ts(&ts); - v_buf = fimc_active_queue_pop(cap); - tv = &v_buf->vb.v4l2_buf.timestamp; - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; - v_buf->vb.v4l2_buf.sequence = cap->frame_count++; + v4l2_get_timestamp(&v_buf->vb.timestamp); + v_buf->vb.sequence = cap->frame_count++; - vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&v_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } if (!list_empty(&cap->pending_buf_q)) { @@ -233,7 +227,7 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) list_for_each_entry(v_buf, &cap->active_buf_q, list) { if (v_buf->index != index) continue; - vaddr = vb2_plane_vaddr(&v_buf->vb, plane); + vaddr = vb2_plane_vaddr(&v_buf->vb.vb2_buf, plane); v4l2_subdev_call(csis, video, s_rx_buffer, vaddr, &size); break; @@ -338,16 +332,17 @@ int fimc_capture_resume(struct fimc_dev *fimc) if (list_empty(&vid_cap->pending_buf_q)) break; buf = fimc_pending_queue_pop(vid_cap); - buffer_queue(&buf->vb); + buffer_queue(&buf->vb.vb2_buf); } return 0; } -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { + const struct v4l2_format *pfmt = parg; const struct v4l2_pix_format_mplane *pixm = NULL; struct fimc_ctx *ctx = vq->drv_priv; struct fimc_frame *frame = &ctx->d_frame; @@ -410,8 +405,9 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct fimc_vid_buffer *buf - = container_of(vb, struct fimc_vid_buffer, vb); + = container_of(vbuf, struct fimc_vid_buffer, vb); struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; @@ -420,7 +416,7 @@ static void buffer_queue(struct vb2_buffer *vb) int min_bufs; spin_lock_irqsave(&fimc->slock, flags); - fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr); + fimc_prepare_addr(ctx, &buf->vb.vb2_buf, &ctx->d_frame, &buf->paddr); if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) && !test_bit(ST_CAPT_STREAM, &fimc->state) && @@ -1472,7 +1468,8 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, if (!list_empty(&fimc->vid_cap.active_buf_q)) { buf = list_entry(fimc->vid_cap.active_buf_q.next, struct fimc_vid_buffer, list); - vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, + *((u32 *)arg)); } fimc_capture_irq_handler(fimc, 1); fimc_deactivate_capture(fimc); diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-core.c b/kernel/drivers/media/platform/exynos4-is/fimc-core.c index 1101c41ac..cef2a7f07 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-core.c +++ b/kernel/drivers/media/platform/exynos4-is/fimc-core.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include "fimc-core.h" diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-core.h b/kernel/drivers/media/platform/exynos4-is/fimc-core.h index 7328f0845..d336fa291 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-core.h +++ b/kernel/drivers/media/platform/exynos4-is/fimc-core.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -224,7 +224,7 @@ struct fimc_addr { * @index: buffer index for the output DMA engine */ struct fimc_vid_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; struct fimc_addr paddr; int index; diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-is.h b/kernel/drivers/media/platform/exynos4-is/fimc-is.h index e0be691af..386eb49ec 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-is.h +++ b/kernel/drivers/media/platform/exynos4-is/fimc-is.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "fimc-isp.h" diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.c b/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.c index 76b6b4d14..6e6648446 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include @@ -39,10 +39,11 @@ #include "fimc-is-param.h" static int isp_video_capture_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *pfmt, + const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { + const struct v4l2_format *pfmt = parg; struct fimc_isp *isp = vb2_get_drv_priv(vq); struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; const struct v4l2_pix_format_mplane *pixm = NULL; @@ -194,10 +195,11 @@ static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); struct fimc_is_video *video = &isp->video_capture; struct fimc_is *is = fimc_isp_to_is(isp); - struct isp_video_buf *ivb = to_isp_video_buf(vb); + struct isp_video_buf *ivb = to_isp_video_buf(vbuf); unsigned long flags; unsigned int i; @@ -220,7 +222,7 @@ static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) isp_dbg(2, &video->ve.vdev, "dma_buf %pad (%d/%d/%d) addr: %pad\n", - &buf_index, ivb->index, i, vb->v4l2_buf.index, + &buf_index, ivb->index, i, vb->index, &ivb->dma_addr[i]); } @@ -242,7 +244,7 @@ static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) void fimc_isp_video_irq_handler(struct fimc_is *is) { struct fimc_is_video *video = &is->isp.video_capture; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; int buf_index; /* TODO: Ensure the DMA is really stopped in stop_streaming callback */ @@ -250,10 +252,10 @@ void fimc_isp_video_irq_handler(struct fimc_is *is) return; buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count; - vb = &video->buffers[buf_index]->vb; + vbuf = &video->buffers[buf_index]->vb; - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vbuf->timestamp); + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); video->buf_mask &= ~BIT(buf_index); fimc_is_hw_set_isp_buf_mask(is, video->buf_mask); diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.h b/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.h index 98c662654..f79a1b348 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.h +++ b/kernel/drivers/media/platform/exynos4-is/fimc-isp-video.h @@ -11,7 +11,7 @@ #ifndef FIMC_ISP_VIDEO__ #define FIMC_ISP_VIDEO__ -#include +#include #include "fimc-isp.h" #ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-isp.h b/kernel/drivers/media/platform/exynos4-is/fimc-isp.h index b99be09b4..c2d25df85 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/kernel/drivers/media/platform/exynos4-is/fimc-isp.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -102,7 +102,7 @@ struct fimc_isp_ctrls { }; struct isp_video_buf { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES]; unsigned int index; }; diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-lite.c b/kernel/drivers/media/platform/exynos4-is/fimc-lite.c index ca6261a86..60660c3a5 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/kernel/drivers/media/platform/exynos4-is/fimc-lite.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -200,7 +200,7 @@ static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) /* Release unused buffers */ while (!suspend && !list_empty(&fimc->pending_buf_q)) { buf = fimc_lite_pending_queue_pop(fimc); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } /* If suspending put unused buffers onto pending queue */ while (!list_empty(&fimc->active_buf_q)) { @@ -208,7 +208,7 @@ static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) if (suspend) fimc_lite_pending_queue_add(fimc, buf); else - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&fimc->slock, flags); @@ -254,8 +254,6 @@ static irqreturn_t flite_irq_handler(int irq, void *priv) struct fimc_lite *fimc = priv; struct flite_buffer *vbuf; unsigned long flags; - struct timeval *tv; - struct timespec ts; u32 intsrc; spin_lock_irqsave(&fimc->slock, flags); @@ -294,13 +292,10 @@ static irqreturn_t flite_irq_handler(int irq, void *priv) test_bit(ST_FLITE_RUN, &fimc->state) && !list_empty(&fimc->active_buf_q)) { vbuf = fimc_lite_active_queue_pop(fimc); - ktime_get_ts(&ts); - tv = &vbuf->vb.v4l2_buf.timestamp; - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; - vbuf->vb.v4l2_buf.sequence = fimc->frame_count++; + v4l2_get_timestamp(&vbuf->vb.timestamp); + vbuf->vb.sequence = fimc->frame_count++; flite_hw_mask_dma_buffer(fimc, vbuf->index); - vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); } if (test_bit(ST_FLITE_CONFIG, &fimc->state)) @@ -360,10 +355,11 @@ static void stop_streaming(struct vb2_queue *q) fimc_lite_stop_capture(fimc, false); } -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { + const struct v4l2_format *pfmt = parg; const struct v4l2_pix_format_mplane *pixm = NULL; struct fimc_lite *fimc = vq->drv_priv; struct flite_frame *frame = &fimc->out_frame; @@ -422,8 +418,9 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct flite_buffer *buf - = container_of(vb, struct flite_buffer, vb); + = container_of(vbuf, struct flite_buffer, vb); struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue); unsigned long flags; @@ -1637,7 +1634,7 @@ static int fimc_lite_resume(struct device *dev) if (list_empty(&fimc->pending_buf_q)) break; buf = fimc_lite_pending_queue_pop(fimc); - buffer_queue(&buf->vb); + buffer_queue(&buf->vb.vb2_buf); } return 0; } diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-lite.h b/kernel/drivers/media/platform/exynos4-is/fimc-lite.h index ea19dc7be..b302305de 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/kernel/drivers/media/platform/exynos4-is/fimc-lite.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -100,7 +100,7 @@ struct flite_frame { * @index: DMA start address register's index */ struct flite_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; dma_addr_t paddr; unsigned short index; diff --git a/kernel/drivers/media/platform/exynos4-is/fimc-m2m.c b/kernel/drivers/media/platform/exynos4-is/fimc-m2m.c index 0ad1b6f84..4d1d64a46 100644 --- a/kernel/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/kernel/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "common.h" @@ -42,7 +42,7 @@ static unsigned int get_m2m_fmt_flags(unsigned int stream_type) void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) { - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; if (!ctx || !ctx->fh.m2m_ctx) return; @@ -99,7 +99,7 @@ static void stop_streaming(struct vb2_queue *q) static void fimc_device_run(void *priv) { - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; struct fimc_ctx *ctx = priv; struct fimc_frame *sf, *df; struct fimc_dev *fimc; @@ -123,19 +123,19 @@ static void fimc_device_run(void *priv) } src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); + ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->paddr); if (ret) goto dma_unlock; dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); + ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->paddr); if (ret) goto dma_unlock; - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; - dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.flags |= - src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->timestamp = src_vb->timestamp; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; /* Reconfigure hardware if the context has changed. */ if (fimc->m2m.ctx != ctx) { @@ -176,7 +176,7 @@ static void fimc_job_abort(void *priv) fimc_m2m_shutdown(priv); } -static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int fimc_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { @@ -188,7 +188,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, if (IS_ERR(f)) return PTR_ERR(f); /* - * Return number of non-contigous planes (plane buffers) + * Return number of non-contiguous planes (plane buffers) * depending on the configured color format. */ if (!f->fmt) @@ -220,8 +220,9 @@ static int fimc_buf_prepare(struct vb2_buffer *vb) static void fimc_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static struct vb2_ops fimc_qops = { diff --git a/kernel/drivers/media/platform/exynos4-is/media-dev.c b/kernel/drivers/media/platform/exynos4-is/media-dev.c index f315ef946..4f5586a4c 100644 --- a/kernel/drivers/media/platform/exynos4-is/media-dev.c +++ b/kernel/drivers/media/platform/exynos4-is/media-dev.c @@ -1451,7 +1451,7 @@ static int fimc_md_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id fimc_driver_ids[] __always_unused = { +static const struct platform_device_id fimc_driver_ids[] __always_unused = { { .name = "s5p-fimc-md" }, { }, }; diff --git a/kernel/drivers/media/platform/exynos4-is/mipi-csis.c b/kernel/drivers/media/platform/exynos4-is/mipi-csis.c index d74e1bec3..4b85105dc 100644 --- a/kernel/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/kernel/drivers/media/platform/exynos4-is/mipi-csis.c @@ -706,7 +706,8 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) else offset = S5PCSIS_PKTDATA_ODD; - memcpy(pktbuf->data, state->regs + offset, pktbuf->len); + memcpy(pktbuf->data, (u8 __force *)state->regs + offset, + pktbuf->len); pktbuf->data = NULL; rmb(); } diff --git a/kernel/drivers/media/platform/fsl-viu.c b/kernel/drivers/media/platform/fsl-viu.c index bbf428104..ae8c6b35a 100644 --- a/kernel/drivers/media/platform/fsl-viu.c +++ b/kernel/drivers/media/platform/fsl-viu.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #define DRV_NAME "fsl_viu" @@ -40,49 +43,6 @@ /* I2C address of video decoder chip is 0x4A */ #define VIU_VIDEO_DECODER_ADDR 0x25 -/* supported controls */ -static struct v4l2_queryctrl viu_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 127, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 0x10, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 127, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - } -}; - -static int qctl_regs[ARRAY_SIZE(viu_qctrl)]; - static int info_level; #define dprintk(level, fmt, arg...) \ @@ -95,7 +55,6 @@ static int info_level; * Basic structures */ struct viu_fmt { - char name[32]; u32 fourcc; /* v4l2 format id */ u32 pixelformat; int depth; @@ -103,12 +62,10 @@ struct viu_fmt { static struct viu_fmt formats[] = { { - .name = "RGB-16 (5/B-6/G-5/R)", .fourcc = V4L2_PIX_FMT_RGB565, .pixelformat = V4L2_PIX_FMT_RGB565, .depth = 16, }, { - .name = "RGB-32 (A-R-G-B)", .fourcc = V4L2_PIX_FMT_RGB32, .pixelformat = V4L2_PIX_FMT_RGB32, .depth = 32, @@ -156,6 +113,7 @@ struct viu_reg { struct viu_dev { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct mutex lock; spinlock_t slock; int users; @@ -195,6 +153,8 @@ struct viu_dev { }; struct viu_fh { + /* must remain the first field of this struct */ + struct v4l2_fh fh; struct viu_dev *dev; /* video capture */ @@ -604,6 +564,7 @@ static int vidioc_querycap(struct file *file, void *priv, { strcpy(cap->driver, "viu"); strcpy(cap->card, "viu"); + strcpy(cap->bus_info, "platform:viu"); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OVERLAY | @@ -617,10 +578,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, { int index = f->index; - if (f->index > NUM_FORMATS) + if (f->index >= NUM_FORMATS) return -EINVAL; - strlcpy(f->description, formats[index].name, sizeof(f->description)); f->pixelformat = formats[index].fourcc; return 0; } @@ -637,6 +597,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = fh->sizeimage; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -644,7 +605,6 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct viu_fmt *fmt; - enum v4l2_field field; unsigned int maxw, maxh; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -654,19 +614,10 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, return -EINVAL; } - field = f->fmt.pix.field; - - if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_INTERLACED; - } else if (field != V4L2_FIELD_INTERLACED) { - dprintk(1, "Field type invalid.\n"); - return -EINVAL; - } - maxw = norm_maxw(); maxh = norm_maxh(); - f->fmt.pix.field = field; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; if (f->fmt.pix.height > maxh) @@ -678,6 +629,8 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -698,7 +651,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, fh->sizeimage = f->fmt.pix.sizeimage; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name); return 0; } @@ -764,8 +716,8 @@ static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) { int bpp; - dprintk(1, "%s %dx%d %s\n", __func__, - fh->win.w.width, fh->win.w.height, dev->ovfmt->name); + dprintk(1, "%s %dx%d\n", __func__, + fh->win.w.width, fh->win.w.height); reg_val.status_cfg = 0; @@ -1002,58 +954,13 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct viu_fh *fh = priv; - if (i > 1) + if (i) return -EINVAL; decoder_call(fh->dev, video, s_routing, i, 0, 0); return 0; } -/* Controls */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (qc->id && qc->id == viu_qctrl[i].id) { - memcpy(qc, &(viu_qctrl[i]), sizeof(*qc)); - return 0; - } - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (ctrl->id == viu_qctrl[i].id) { - ctrl->value = qctl_regs[i]; - return 0; - } - } - return -EINVAL; -} -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (ctrl->id == viu_qctrl[i].id) { - if (ctrl->value < viu_qctrl[i].minimum - || ctrl->value > viu_qctrl[i].maximum) - return -ERANGE; - qctl_regs[i] = ctrl->value; - return 0; - } - } - return -EINVAL; -} - inline void viu_activate_next_buf(struct viu_dev *dev, struct viu_dmaqueue *viuq) { @@ -1265,7 +1172,6 @@ static int viu_open(struct file *file) struct viu_reg *vr; int minor = vdev->minor; u32 status_cfg; - int i; dprintk(1, "viu: open (minor=%d)\n", minor); @@ -1293,6 +1199,7 @@ static int viu_open(struct file *file) return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; @@ -1303,10 +1210,6 @@ static int viu_open(struct file *file) dev->crop_current.width = fh->width; dev->crop_current.height = fh->height; - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) - qctl_regs[i] = viu_qctrl[i].default_value; - dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", (unsigned long)fh, (unsigned long)dev, (unsigned long)&dev->vidq); @@ -1332,6 +1235,7 @@ static int viu_open(struct file *file) fh->type, V4L2_FIELD_INTERLACED, sizeof(struct viu_buf), fh, &fh->dev->lock); + v4l2_fh_add(&fh->fh); mutex_unlock(&dev->lock); return 0; } @@ -1364,13 +1268,17 @@ static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait) struct viu_fh *fh = file->private_data; struct videobuf_queue *q = &fh->vb_vidq; struct viu_dev *dev = fh->dev; - unsigned int res; + unsigned long req_events = poll_requested_events(wait); + unsigned int res = v4l2_ctrl_poll(file, wait); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; + if (!(req_events & (POLLIN | POLLRDNORM))) + return res; + mutex_lock(&dev->lock); - res = videobuf_poll_stream(file, q, wait); + res |= videobuf_poll_stream(file, q, wait); mutex_unlock(&dev->lock); return res; } @@ -1385,6 +1293,8 @@ static int viu_release(struct file *file) viu_stop_dma(dev); videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); mutex_unlock(&dev->lock); kfree(fh); @@ -1463,11 +1373,11 @@ static const struct v4l2_ioctl_ops viu_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device viu_template = { @@ -1543,6 +1453,16 @@ static int viu_of_probe(struct platform_device *op) } ad = i2c_get_adapter(0); + + v4l2_ctrl_handler_init(&viu_dev->hdl, 5); + if (viu_dev->hdl.error) { + ret = viu_dev->hdl.error; + dev_err(&op->dev, "couldn't register control\n"); + goto err_vdev; + } + /* This control handler will inherit the control(s) from the + sub-device(s). */ + viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl; viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); @@ -1559,7 +1479,7 @@ static int viu_of_probe(struct platform_device *op) goto err_vdev; } - memcpy(vdev, &viu_template, sizeof(viu_template)); + *vdev = viu_template; vdev->v4l2_dev = &viu_dev->v4l2_dev; @@ -1614,6 +1534,7 @@ err_irq: err_clk: video_unregister_device(viu_dev->vdev); err_vdev: + v4l2_ctrl_handler_free(&viu_dev->hdl); mutex_unlock(&viu_dev->lock); i2c_put_adapter(ad); v4l2_device_unregister(&viu_dev->v4l2_dev); @@ -1635,6 +1556,7 @@ static int viu_of_remove(struct platform_device *op) clk_disable_unprepare(dev->clk); + v4l2_ctrl_handler_free(&dev->hdl); video_unregister_device(dev->vdev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&dev->v4l2_dev); @@ -1664,7 +1586,7 @@ static int viu_resume(struct platform_device *op) /* * Initialization and module stuff */ -static struct of_device_id mpc512x_viu_of_match[] = { +static const struct of_device_id mpc512x_viu_of_match[] = { { .compatible = "fsl,mpc5121-viu", }, diff --git a/kernel/drivers/media/platform/m2m-deinterlace.c b/kernel/drivers/media/platform/m2m-deinterlace.c index 92d954973..29973f9bf 100644 --- a/kernel/drivers/media/platform/m2m-deinterlace.c +++ b/kernel/drivers/media/platform/m2m-deinterlace.c @@ -200,18 +200,18 @@ static void dma_callback(void *data) { struct deinterlace_ctx *curr_ctx = data; struct deinterlace_dev *pcdev = curr_ctx->dev; - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; atomic_set(&pcdev->busy, 0); src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; - dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.flags |= - src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode; + dst_vb->timestamp = src_vb->timestamp; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->timecode = src_vb->timecode; v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -225,7 +225,7 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op, int do_callback) { struct deinterlace_q_data *s_q_data; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct deinterlace_dev *pcdev = ctx->dev; struct dma_chan *chan = pcdev->dma_chan; struct dma_device *dmadev = chan->device; @@ -243,8 +243,9 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op, s_height = s_q_data->height; s_size = s_width * s_height; - p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(src_buf, 0); - p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(dst_buf, 0); + p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, + 0); if (!p_in || !p_out) { v4l2_err(&pcdev->v4l2_dev, "Acquiring kernel pointers to buffers failed\n"); @@ -797,7 +798,7 @@ struct vb2_dc_conf { }; static int deinterlace_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -849,8 +850,10 @@ static int deinterlace_buf_prepare(struct vb2_buffer *vb) static void deinterlace_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + + v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } static struct vb2_ops deinterlace_qops = { @@ -1060,7 +1063,6 @@ static int deinterlace_probe(struct platform_device *pdev) return 0; - v4l2_m2m_release(pcdev->m2m_dev); err_m2m: video_unregister_device(&pcdev->vfd); err_ctx: diff --git a/kernel/drivers/media/platform/marvell-ccic/cafe-driver.c b/kernel/drivers/media/platform/marvell-ccic/cafe-driver.c index 562845361..77890bd0d 100644 --- a/kernel/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/kernel/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -339,17 +339,21 @@ static int cafe_smbus_setup(struct cafe_camera *cam) adap = kzalloc(sizeof(*adap), GFP_KERNEL); if (adap == NULL) return -ENOMEM; - cam->mcam.i2c_adapter = adap; - cafe_smbus_enable_irq(cam); adap->owner = THIS_MODULE; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); adap->dev.parent = &cam->pdev->dev; i2c_set_adapdata(adap, cam); ret = i2c_add_adapter(adap); - if (ret) + if (ret) { printk(KERN_ERR "Unable to register cafe i2c adapter\n"); - return ret; + kfree(adap); + return ret; + } + + cam->mcam.i2c_adapter = adap; + cafe_smbus_enable_irq(cam); + return 0; } static void cafe_smbus_shutdown(struct cafe_camera *cam) @@ -476,6 +480,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, mcam->plat_power_up = cafe_ctlr_power_up; mcam->plat_power_down = cafe_ctlr_power_down; mcam->dev = &pdev->dev; + snprintf(mcam->bus_info, sizeof(mcam->bus_info), "PCI:%s", pci_name(pdev)); /* * Set the clock speed for the XO 1; I don't believe this * driver has ever run anywhere else. diff --git a/kernel/drivers/media/platform/marvell-ccic/mcam-core.c b/kernel/drivers/media/platform/marvell-ccic/mcam-core.c index 110fd70c7..aa2b44041 100644 --- a/kernel/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/kernel/drivers/media/platform/marvell-ccic/mcam-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -122,30 +123,23 @@ static struct mcam_format_struct { .bpp = 2, .planar = false, }, - { - .desc = "YUV 4:2:2 PLANAR", - .pixelformat = V4L2_PIX_FMT_YUV422P, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 2, - .planar = true, - }, { .desc = "YUV 4:2:0 PLANAR", .pixelformat = V4L2_PIX_FMT_YUV420, .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 2, + .bpp = 1, .planar = true, }, { .desc = "YVU 4:2:0 PLANAR", .pixelformat = V4L2_PIX_FMT_YVU420, .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 2, + .bpp = 1, .planar = true, }, { - .desc = "RGB 444", - .pixelformat = V4L2_PIX_FMT_RGB444, + .desc = "XRGB 444", + .pixelformat = V4L2_PIX_FMT_XRGB444, .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 2, .planar = false, @@ -188,6 +182,7 @@ static const struct v4l2_pix_format mcam_def_pix_format = { .field = V4L2_FIELD_NONE, .bytesperline = VGA_WIDTH*2, .sizeimage = VGA_WIDTH*VGA_HEIGHT*2, + .colorspace = V4L2_COLORSPACE_SRGB, }; static const u32 mcam_def_mbus_code = MEDIA_BUS_FMT_YUYV8_2X8; @@ -204,27 +199,20 @@ struct mcam_dma_desc { u32 segment_len; }; -struct yuv_pointer_t { - dma_addr_t y; - dma_addr_t u; - dma_addr_t v; -}; - /* * Our buffer type for working with videobuf2. Note that the vb2 - * developers have decreed that struct vb2_buffer must be at the + * developers have decreed that struct vb2_v4l2_buffer must be at the * beginning of this structure. */ struct mcam_vb_buffer { - struct vb2_buffer vb_buf; + struct vb2_v4l2_buffer vb_buf; struct list_head queue; struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */ dma_addr_t dma_desc_pa; /* Descriptor physical address */ int dma_desc_nent; /* Number of mapped descriptors */ - struct yuv_pointer_t yuv_p; }; -static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb) +static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct mcam_vb_buffer, vb_buf); } @@ -233,12 +221,14 @@ static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb) * Hand a completed buffer back to user space. */ static void mcam_buffer_done(struct mcam_camera *cam, int frame, - struct vb2_buffer *vbuf) + struct vb2_v4l2_buffer *vbuf) { - vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage; - vbuf->v4l2_buf.sequence = cam->buf_seq[frame]; - vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage); - vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE); + vbuf->vb2_buf.planes[0].bytesused = cam->pix_format.sizeimage; + vbuf->sequence = cam->buf_seq[frame]; + vbuf->field = V4L2_FIELD_NONE; + v4l2_get_timestamp(&vbuf->timestamp); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, cam->pix_format.sizeimage); + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); } @@ -337,6 +327,43 @@ static void mcam_disable_mipi(struct mcam_camera *mcam) mcam->mipi_enabled = false; } +static bool mcam_fmt_is_planar(__u32 pfmt) +{ + struct mcam_format_struct *f; + + f = mcam_find_format(pfmt); + return f->planar; +} + +static void mcam_write_yuv_bases(struct mcam_camera *cam, + unsigned frame, dma_addr_t base) +{ + struct v4l2_pix_format *fmt = &cam->pix_format; + u32 pixel_count = fmt->width * fmt->height; + dma_addr_t y, u = 0, v = 0; + + y = base; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_YUV420: + u = y + pixel_count; + v = u + pixel_count / 4; + break; + case V4L2_PIX_FMT_YVU420: + v = y + pixel_count; + u = v + pixel_count / 4; + break; + default: + break; + } + + mcam_reg_write(cam, REG_Y0BAR + frame * 4, y); + if (mcam_fmt_is_planar(fmt->pixelformat)) { + mcam_reg_write(cam, REG_U0BAR + frame * 4, u); + mcam_reg_write(cam, REG_V0BAR + frame * 4, v); + } +} + /* ------------------------------------------------------------------- */ #ifdef MCAM_MODE_VMALLOC @@ -407,15 +434,14 @@ static void mcam_free_dma_bufs(struct mcam_camera *cam) static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) { /* - * Store the first two Y buffers (we aren't supporting - * planar formats for now, so no UV bufs). Then either + * Store the first two YUV buffers. Then either * set the third if it exists, or tell the controller * to just use two. */ - mcam_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]); - mcam_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]); + mcam_write_yuv_bases(cam, 0, cam->dma_handles[0]); + mcam_write_yuv_bases(cam, 1, cam->dma_handles[1]); if (cam->nbufs > 2) { - mcam_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]); + mcam_write_yuv_bases(cam, 2, cam->dma_handles[2]); mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); } else mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); @@ -456,7 +482,8 @@ static void mcam_frame_tasklet(unsigned long data) * Drop the lock during the big copy. This *should* be safe... */ spin_unlock_irqrestore(&cam->dev_lock, flags); - memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno], + memcpy(vb2_plane_vaddr(&buf->vb_buf.vb2_buf, 0), + cam->dma_bufs[bufno], cam->pix_format.sizeimage); mcam_buffer_done(cam, bufno, &buf->vb_buf); spin_lock_irqsave(&cam->dev_lock, flags); @@ -510,14 +537,6 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam) * DMA-contiguous code. */ -static bool mcam_fmt_is_planar(__u32 pfmt) -{ - struct mcam_format_struct *f; - - f = mcam_find_format(pfmt); - return f->planar; -} - /* * Set up a contiguous buffer for the given frame. Here also is where * the underrun strategy is set: if there is no buffer available, reuse @@ -529,10 +548,8 @@ static bool mcam_fmt_is_planar(__u32 pfmt) static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) { struct mcam_vb_buffer *buf; - struct v4l2_pix_format *fmt = &cam->pix_format; dma_addr_t dma_handle; - u32 pixel_count = fmt->width * fmt->height; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; /* * If there are no available buffers, go into single mode @@ -554,33 +571,8 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) cam->vb_bufs[frame] = buf; vb = &buf->vb_buf; - dma_handle = vb2_dma_contig_plane_dma_addr(vb, 0); - buf->yuv_p.y = dma_handle; - - switch (cam->pix_format.pixelformat) { - case V4L2_PIX_FMT_YUV422P: - buf->yuv_p.u = buf->yuv_p.y + pixel_count; - buf->yuv_p.v = buf->yuv_p.u + pixel_count / 2; - break; - case V4L2_PIX_FMT_YUV420: - buf->yuv_p.u = buf->yuv_p.y + pixel_count; - buf->yuv_p.v = buf->yuv_p.u + pixel_count / 4; - break; - case V4L2_PIX_FMT_YVU420: - buf->yuv_p.v = buf->yuv_p.y + pixel_count; - buf->yuv_p.u = buf->yuv_p.v + pixel_count / 4; - break; - default: - break; - } - - mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, buf->yuv_p.y); - if (mcam_fmt_is_planar(fmt->pixelformat)) { - mcam_reg_write(cam, frame == 0 ? - REG_U0BAR : REG_U1BAR, buf->yuv_p.u); - mcam_reg_write(cam, frame == 0 ? - REG_V0BAR : REG_V1BAR, buf->yuv_p.v); - } + dma_handle = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); + mcam_write_yuv_bases(cam, frame, dma_handle); } /* @@ -603,6 +595,7 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) { cam->frame_state.delivered++; + cam->vb_bufs[frame] = NULL; mcam_buffer_done(cam, frame, &buf->vb_buf); } mcam_set_contig_buffer(cam, frame); @@ -752,12 +745,6 @@ static void mcam_ctlr_image(struct mcam_camera *cam) widthy = fmt->width * 2; widthuv = 0; break; - case V4L2_PIX_FMT_JPEG: - imgsz_h = (fmt->sizeimage / fmt->bytesperline) << IMGSZ_V_SHIFT; - widthy = fmt->bytesperline; - widthuv = 0; - break; - case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: widthy = fmt->width; @@ -766,6 +753,7 @@ static void mcam_ctlr_image(struct mcam_camera *cam) default: widthy = fmt->bytesperline; widthuv = 0; + break; } mcam_reg_write_mask(cam, REG_IMGPITCH, widthuv << 16 | widthy, @@ -777,10 +765,6 @@ static void mcam_ctlr_image(struct mcam_camera *cam) * Tell the controller about the image format we are using. */ switch (fmt->pixelformat) { - case V4L2_PIX_FMT_YUV422P: - mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_YUV | C0_YUV_PLANAR | C0_YUVE_YVYU, C0_DF_MASK); - break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: mcam_reg_write_mask(cam, REG_CTRL0, @@ -794,19 +778,18 @@ static void mcam_ctlr_image(struct mcam_camera *cam) mcam_reg_write_mask(cam, REG_CTRL0, C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_SWAP24, C0_DF_MASK); break; - case V4L2_PIX_FMT_JPEG: - mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK); - break; - case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_XRGB444: mcam_reg_write_mask(cam, REG_CTRL0, - C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB, C0_DF_MASK); - /* Alpha value? */ + C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XBGR, C0_DF_MASK); break; case V4L2_PIX_FMT_RGB565: mcam_reg_write_mask(cam, REG_CTRL0, C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR, C0_DF_MASK); break; + case V4L2_PIX_FMT_SBGGR8: + mcam_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB | C0_RGB5_GRBG, C0_DF_MASK); + break; default: cam_err(cam, "camera: unknown format: %#x\n", fmt->pixelformat); break; @@ -969,7 +952,6 @@ static int mcam_cam_init(struct mcam_camera *cam) { int ret; - mutex_lock(&cam->s_mutex); if (cam->state != S_NOTREADY) cam_warn(cam, "Cam init with device in funky state %d", cam->state); @@ -977,7 +959,6 @@ static int mcam_cam_init(struct mcam_camera *cam) /* Get/set parameters? */ cam->state = S_IDLE; mcam_ctlr_power_down(cam); - mutex_unlock(&cam->s_mutex); return ret; } @@ -998,13 +979,15 @@ static int mcam_cam_set_flip(struct mcam_camera *cam) static int mcam_cam_configure(struct mcam_camera *cam) { - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; - v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code); + v4l2_fill_mbus_format(&format.format, &cam->pix_format, cam->mbus_code); ret = sensor_call(cam, core, init, 0); if (ret == 0) - ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt); + ret = sensor_call(cam, pad, set_fmt, NULL, &format); /* * OV7670 does weird things if flip is set *before* format... */ @@ -1066,14 +1049,17 @@ static int mcam_read_setup(struct mcam_camera *cam) */ static int mcam_vb_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbufs, + const void *parg, unsigned int *nbufs, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct mcam_camera *cam = vb2_get_drv_priv(vq); int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2; - sizes[0] = cam->pix_format.sizeimage; + if (fmt && fmt->fmt.pix.sizeimage < cam->pix_format.sizeimage) + return -EINVAL; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : cam->pix_format.sizeimage; *num_planes = 1; /* Someday we have to support planar formats... */ if (*nbufs < minbufs) *nbufs = minbufs; @@ -1087,7 +1073,8 @@ static int mcam_vb_queue_setup(struct vb2_queue *vq, static void mcam_vb_buf_queue(struct vb2_buffer *vb) { - struct mcam_vb_buffer *mvb = vb_to_mvb(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mcam_vb_buffer *mvb = vb_to_mvb(vbuf); struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); unsigned long flags; int start; @@ -1102,6 +1089,30 @@ static void mcam_vb_buf_queue(struct vb2_buffer *vb) mcam_read_setup(cam); } +static void mcam_vb_requeue_bufs(struct vb2_queue *vq, + enum vb2_buffer_state state) +{ + struct mcam_camera *cam = vb2_get_drv_priv(vq); + struct mcam_vb_buffer *buf, *node; + unsigned long flags; + unsigned i; + + spin_lock_irqsave(&cam->dev_lock, flags); + list_for_each_entry_safe(buf, node, &cam->buffers, queue) { + vb2_buffer_done(&buf->vb_buf.vb2_buf, state); + list_del(&buf->queue); + } + for (i = 0; i < MAX_DMA_BUFS; i++) { + buf = cam->vb_bufs[i]; + + if (buf) { + vb2_buffer_done(&buf->vb_buf.vb2_buf, state); + cam->vb_bufs[i] = NULL; + } + } + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + /* * These need to be called with the mutex held from vb2 */ @@ -1109,11 +1120,15 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) { struct mcam_camera *cam = vb2_get_drv_priv(vq); unsigned int frame; + int ret; if (cam->state != S_IDLE) { - INIT_LIST_HEAD(&cam->buffers); + mcam_vb_requeue_bufs(vq, VB2_BUF_STATE_QUEUED); return -EINVAL; } + cam->frame_state.frames = 0; + cam->frame_state.singles = 0; + cam->frame_state.delivered = 0; cam->sequence = 0; /* * Videobuf2 sneakily hoards all the buffers and won't @@ -1134,14 +1149,19 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) for (frame = 0; frame < cam->nbufs; frame++) clear_bit(CF_FRAME_SOF0 + frame, &cam->flags); - return mcam_read_setup(cam); + ret = mcam_read_setup(cam); + if (ret) + mcam_vb_requeue_bufs(vq, VB2_BUF_STATE_QUEUED); + return ret; } static void mcam_vb_stop_streaming(struct vb2_queue *vq) { struct mcam_camera *cam = vb2_get_drv_priv(vq); - unsigned long flags; + cam_dbg(cam, "stop_streaming: %d frames, %d singles, %d delivered\n", + cam->frame_state.frames, cam->frame_state.singles, + cam->frame_state.delivered); if (cam->state == S_BUFWAIT) { /* They never gave us buffers */ cam->state = S_IDLE; @@ -1160,9 +1180,7 @@ static void mcam_vb_stop_streaming(struct vb2_queue *vq) * VB2 reclaims the buffers, so we need to forget * about them. */ - spin_lock_irqsave(&cam->dev_lock, flags); - INIT_LIST_HEAD(&cam->buffers); - spin_unlock_irqrestore(&cam->dev_lock, flags); + mcam_vb_requeue_bufs(vq, VB2_BUF_STATE_ERROR); } @@ -1183,7 +1201,8 @@ static const struct vb2_ops mcam_vb2_ops = { */ static int mcam_vb_sg_buf_init(struct vb2_buffer *vb) { - struct mcam_vb_buffer *mvb = vb_to_mvb(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mcam_vb_buffer *mvb = vb_to_mvb(vbuf); struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1; @@ -1199,7 +1218,8 @@ static int mcam_vb_sg_buf_init(struct vb2_buffer *vb) static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb) { - struct mcam_vb_buffer *mvb = vb_to_mvb(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mcam_vb_buffer *mvb = vb_to_mvb(vbuf); struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0); struct mcam_dma_desc *desc = mvb->dma_desc; struct scatterlist *sg; @@ -1215,8 +1235,9 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb) static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); - struct mcam_vb_buffer *mvb = vb_to_mvb(vb); + struct mcam_vb_buffer *mvb = vb_to_mvb(vbuf); int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1; dma_free_coherent(cam->dev, ndesc * sizeof(struct mcam_dma_desc), @@ -1246,14 +1267,15 @@ static int mcam_setup_vb2(struct mcam_camera *cam) vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vq->drv_priv = cam; vq->lock = &cam->s_mutex; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + vq->buf_struct_size = sizeof(struct mcam_vb_buffer); INIT_LIST_HEAD(&cam->buffers); switch (cam->buffer_mode) { case B_DMA_contig: #ifdef MCAM_MODE_DMA_CONTIG vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_dma_contig_memops; - vq->buf_struct_size = sizeof(struct mcam_vb_buffer); - vq->io_modes = VB2_MMAP | VB2_USERPTR; cam->dma_setup = mcam_ctlr_dma_contig; cam->frame_complete = mcam_dma_contig_done; cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev); @@ -1265,8 +1287,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) #ifdef MCAM_MODE_DMA_SG vq->ops = &mcam_vb2_sg_ops; vq->mem_ops = &vb2_dma_sg_memops; - vq->buf_struct_size = sizeof(struct mcam_vb_buffer); - vq->io_modes = VB2_MMAP | VB2_USERPTR; cam->dma_setup = mcam_ctlr_dma_sg; cam->frame_complete = mcam_dma_sg_done; cam->vb_alloc_ctx_sg = vb2_dma_sg_init_ctx(cam->dev); @@ -1280,8 +1300,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) (unsigned long) cam); vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_vmalloc_memops; - vq->buf_struct_size = sizeof(struct mcam_vb_buffer); - vq->io_modes = VB2_MMAP; cam->dma_setup = mcam_ctlr_dma_vmalloc; cam->frame_complete = mcam_vmalloc_done; #endif @@ -1292,7 +1310,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam) static void mcam_cleanup_vb2(struct mcam_camera *cam) { - vb2_queue_release(&cam->vb_queue); #ifdef MCAM_MODE_DMA_CONTIG if (cam->buffer_mode == B_DMA_contig) vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx); @@ -1309,86 +1326,14 @@ static void mcam_cleanup_vb2(struct mcam_camera *cam) * The long list of V4L2 ioctl() operations. */ -static int mcam_vidioc_streamon(struct file *filp, void *priv, - enum v4l2_buf_type type) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_streamon(&cam->vb_queue, type); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_streamoff(struct file *filp, void *priv, - enum v4l2_buf_type type) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_streamoff(&cam->vb_queue, type); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_reqbufs(struct file *filp, void *priv, - struct v4l2_requestbuffers *req) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_reqbufs(&cam->vb_queue, req); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_querybuf(struct file *filp, void *priv, - struct v4l2_buffer *buf) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_querybuf(&cam->vb_queue, buf); - mutex_unlock(&cam->s_mutex); - return ret; -} - -static int mcam_vidioc_qbuf(struct file *filp, void *priv, - struct v4l2_buffer *buf) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_qbuf(&cam->vb_queue, buf); - mutex_unlock(&cam->s_mutex); - return ret; -} - -static int mcam_vidioc_dqbuf(struct file *filp, void *priv, - struct v4l2_buffer *buf) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK); - mutex_unlock(&cam->s_mutex); - return ret; -} - static int mcam_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct mcam_camera *cam = video_drvdata(file); + strcpy(cap->driver, "marvell_ccic"); strcpy(cap->card, "marvell_ccic"); + strlcpy(cap->bus_info, cam->bus_info, sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -1410,36 +1355,38 @@ static int mcam_vidioc_enum_fmt_vid_cap(struct file *filp, static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmt) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); struct mcam_format_struct *f; struct v4l2_pix_format *pix = &fmt->fmt.pix; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; int ret; f = mcam_find_format(pix->pixelformat); pix->pixelformat = f->pixelformat; - v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code); - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt); - mutex_unlock(&cam->s_mutex); - v4l2_fill_pix_format(pix, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, pix, f->mbus_code); + ret = sensor_call(cam, pad, set_fmt, &pad_cfg, &format); + v4l2_fill_pix_format(pix, &format.format); + pix->bytesperline = pix->width * f->bpp; switch (f->pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - pix->bytesperline = pix->width * 3 / 2; + pix->sizeimage = pix->height * pix->bytesperline * 3 / 2; break; default: - pix->bytesperline = pix->width * f->bpp; + pix->sizeimage = pix->height * pix->bytesperline; break; } - pix->sizeimage = pix->height * pix->bytesperline; + pix->colorspace = V4L2_COLORSPACE_SRGB; return ret; } static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmt) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); struct mcam_format_struct *f; int ret; @@ -1447,7 +1394,7 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, * Can't do anything if the device is not idle * Also can't if there are streaming buffers in place. */ - if (cam->state != S_IDLE || cam->vb_queue.num_buffers > 0) + if (cam->state != S_IDLE || vb2_is_busy(&cam->vb_queue)) return -EBUSY; f = mcam_find_format(fmt->fmt.pix.pixelformat); @@ -1462,7 +1409,6 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, * Now we start to change things for real, so let's do it * under lock. */ - mutex_lock(&cam->s_mutex); cam->pix_format = fmt->fmt.pix; cam->mbus_code = f->mbus_code; @@ -1476,7 +1422,6 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, } mcam_set_config_needed(cam, 1); out: - mutex_unlock(&cam->s_mutex); return ret; } @@ -1488,7 +1433,7 @@ out: static int mcam_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *f) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); f->fmt.pix = cam->pix_format; return 0; @@ -1504,7 +1449,6 @@ static int mcam_vidioc_enum_input(struct file *filp, void *priv, return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; - input->std = V4L2_STD_ALL; /* Not sure what should go here */ strcpy(input->name, "Camera"); return 0; } @@ -1522,18 +1466,6 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -/* from vivi.c */ -static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a) -{ - return 0; -} - -static int mcam_vidioc_g_std(struct file *filp, void *priv, v4l2_std_id *a) -{ - *a = V4L2_STD_NTSC_M; - return 0; -} - /* * G/S_PARM. Most of this is done by the sensor, but we are * the level which controls the number of read buffers. @@ -1541,12 +1473,10 @@ static int mcam_vidioc_g_std(struct file *filp, void *priv, v4l2_std_id *a) static int mcam_vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parms) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); int ret; - mutex_lock(&cam->s_mutex); ret = sensor_call(cam, video, g_parm, parms); - mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; } @@ -1554,12 +1484,10 @@ static int mcam_vidioc_g_parm(struct file *filp, void *priv, static int mcam_vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parms) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); int ret; - mutex_lock(&cam->s_mutex); ret = sensor_call(cam, video, s_parm, parms); - mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; } @@ -1567,7 +1495,7 @@ static int mcam_vidioc_s_parm(struct file *filp, void *priv, static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, struct v4l2_frmsizeenum *sizes) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); struct mcam_format_struct *f; struct v4l2_subdev_frame_size_enum fse = { .index = sizes->index, @@ -1579,9 +1507,7 @@ static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, if (f->pixelformat != sizes->pixel_format) return -EINVAL; fse.code = f->mbus_code; - mutex_lock(&cam->s_mutex); ret = sensor_call(cam, pad, enum_frame_size, NULL, &fse); - mutex_unlock(&cam->s_mutex); if (ret) return ret; if (fse.min_width == fse.max_width && @@ -1604,7 +1530,7 @@ static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *interval) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(filp); struct mcam_format_struct *f; struct v4l2_subdev_frame_interval_enum fie = { .index = interval->index, @@ -1618,9 +1544,7 @@ static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv, if (f->pixelformat != interval->pixel_format) return -EINVAL; fie.code = f->mbus_code; - mutex_lock(&cam->s_mutex); ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie); - mutex_unlock(&cam->s_mutex); if (ret) return ret; interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; @@ -1632,7 +1556,7 @@ static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv, static int mcam_vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(file); if (reg->reg > cam->regs_size - 4) return -EINVAL; @@ -1644,7 +1568,7 @@ static int mcam_vidioc_g_register(struct file *file, void *priv, static int mcam_vidioc_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { - struct mcam_camera *cam = priv; + struct mcam_camera *cam = video_drvdata(file); if (reg->reg > cam->regs_size - 4) return -EINVAL; @@ -1662,18 +1586,20 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { .vidioc_enum_input = mcam_vidioc_enum_input, .vidioc_g_input = mcam_vidioc_g_input, .vidioc_s_input = mcam_vidioc_s_input, - .vidioc_s_std = mcam_vidioc_s_std, - .vidioc_g_std = mcam_vidioc_g_std, - .vidioc_reqbufs = mcam_vidioc_reqbufs, - .vidioc_querybuf = mcam_vidioc_querybuf, - .vidioc_qbuf = mcam_vidioc_qbuf, - .vidioc_dqbuf = mcam_vidioc_dqbuf, - .vidioc_streamon = mcam_vidioc_streamon, - .vidioc_streamoff = mcam_vidioc_streamoff, + .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_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_parm = mcam_vidioc_g_parm, .vidioc_s_parm = mcam_vidioc_s_parm, .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes, .vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = mcam_vidioc_g_register, .vidioc_s_register = mcam_vidioc_s_register, @@ -1687,43 +1613,36 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { static int mcam_v4l_open(struct file *filp) { struct mcam_camera *cam = video_drvdata(filp); - int ret = 0; - - filp->private_data = cam; + int ret; - cam->frame_state.frames = 0; - cam->frame_state.singles = 0; - cam->frame_state.delivered = 0; mutex_lock(&cam->s_mutex); - if (cam->users == 0) { - ret = mcam_setup_vb2(cam); - if (ret) - goto out; + ret = v4l2_fh_open(filp); + if (ret) + goto out; + if (v4l2_fh_is_singular_file(filp)) { ret = mcam_ctlr_power_up(cam); if (ret) goto out; __mcam_cam_reset(cam); mcam_set_config_needed(cam, 1); } - (cam->users)++; out: mutex_unlock(&cam->s_mutex); + if (ret) + v4l2_fh_release(filp); return ret; } static int mcam_v4l_release(struct file *filp) { - struct mcam_camera *cam = filp->private_data; + struct mcam_camera *cam = video_drvdata(filp); + bool last_open; - cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", - cam->frame_state.frames, cam->frame_state.singles, - cam->frame_state.delivered); mutex_lock(&cam->s_mutex); - (cam->users)--; - if (cam->users == 0) { - mcam_ctlr_stop_dma(cam); - mcam_cleanup_vb2(cam); + last_open = v4l2_fh_is_singular_file(filp); + _vb2_fop_release(filp, NULL); + if (last_open) { mcam_disable_mipi(cam); mcam_ctlr_power_down(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) @@ -1734,54 +1653,13 @@ static int mcam_v4l_release(struct file *filp) return 0; } -static ssize_t mcam_v4l_read(struct file *filp, - char __user *buffer, size_t len, loff_t *pos) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_read(&cam->vb_queue, buffer, len, pos, - filp->f_flags & O_NONBLOCK); - mutex_unlock(&cam->s_mutex); - return ret; -} - - - -static unsigned int mcam_v4l_poll(struct file *filp, - struct poll_table_struct *pt) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_poll(&cam->vb_queue, filp, pt); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_v4l_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct mcam_camera *cam = filp->private_data; - int ret; - - mutex_lock(&cam->s_mutex); - ret = vb2_mmap(&cam->vb_queue, vma); - mutex_unlock(&cam->s_mutex); - return ret; -} - - - static const struct v4l2_file_operations mcam_v4l_fops = { .owner = THIS_MODULE, .open = mcam_v4l_open, .release = mcam_v4l_release, - .read = mcam_v4l_read, - .poll = mcam_v4l_poll, - .mmap = mcam_v4l_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -1792,8 +1670,6 @@ static const struct v4l2_file_operations mcam_v4l_fops = { */ static struct video_device mcam_v4l_template = { .name = "mcam", - .tvnorms = V4L2_STD_NTSC_M, - .fops = &mcam_v4l_fops, .ioctl_ops = &mcam_v4l_ioctl_ops, .release = video_device_release_empty, @@ -1811,7 +1687,7 @@ static void mcam_frame_complete(struct mcam_camera *cam, int frame) set_bit(frame, &cam->flags); clear_bit(CF_DMA_ACTIVE, &cam->flags); cam->next_buf = frame; - cam->buf_seq[frame] = ++(cam->sequence); + cam->buf_seq[frame] = cam->sequence++; cam->frame_state.frames++; /* * "This should never happen" @@ -1924,9 +1800,16 @@ int mccic_register(struct mcam_camera *cam) mcam_set_config_needed(cam, 1); cam->pix_format = mcam_def_pix_format; cam->mbus_code = mcam_def_mbus_code; - INIT_LIST_HEAD(&cam->buffers); mcam_ctlr_init(cam); + /* + * Get the v4l2 setup done. + */ + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (ret) + goto out_unregister; + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + /* * Try to find the sensor. */ @@ -1943,21 +1826,22 @@ int mccic_register(struct mcam_camera *cam) ret = mcam_cam_init(cam); if (ret) goto out_unregister; - /* - * Get the v4l2 setup done. - */ - ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + + ret = mcam_setup_vb2(cam); if (ret) goto out_unregister; - cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; mutex_lock(&cam->s_mutex); cam->vdev = mcam_v4l_template; cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.lock = &cam->s_mutex; + cam->vdev.queue = &cam->vb_queue; video_set_drvdata(&cam->vdev, cam); ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); - if (ret) - goto out; + if (ret) { + mutex_unlock(&cam->s_mutex); + goto out_unregister; + } /* * If so requested, try to get our DMA buffers now. @@ -1968,11 +1852,11 @@ int mccic_register(struct mcam_camera *cam) " will try again later."); } -out: - v4l2_ctrl_handler_free(&cam->ctrl_handler); mutex_unlock(&cam->s_mutex); - return ret; + return 0; + out_unregister: + v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_device_unregister(&cam->v4l2_dev); return ret; } @@ -1986,11 +1870,11 @@ void mccic_shutdown(struct mcam_camera *cam) * take it down again will wedge the machine, which is frowned * upon. */ - if (cam->users > 0) { + if (!list_empty(&cam->vdev.fh_list)) { cam_warn(cam, "Removing a device with users!\n"); mcam_ctlr_power_down(cam); } - vb2_queue_release(&cam->vb_queue); + mcam_cleanup_vb2(cam); if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); video_unregister_device(&cam->vdev); @@ -2006,7 +1890,7 @@ void mccic_shutdown(struct mcam_camera *cam) void mccic_suspend(struct mcam_camera *cam) { mutex_lock(&cam->s_mutex); - if (cam->users > 0) { + if (!list_empty(&cam->vdev.fh_list)) { enum mcam_state cstate = cam->state; mcam_ctlr_stop_dma(cam); @@ -2021,7 +1905,7 @@ int mccic_resume(struct mcam_camera *cam) int ret = 0; mutex_lock(&cam->s_mutex); - if (cam->users > 0) { + if (!list_empty(&cam->vdev.fh_list)) { ret = mcam_ctlr_power_up(cam); if (ret) { mutex_unlock(&cam->s_mutex); diff --git a/kernel/drivers/media/platform/marvell-ccic/mcam-core.h b/kernel/drivers/media/platform/marvell-ccic/mcam-core.h index 7ffdf4dba..35cd9e5ae 100644 --- a/kernel/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/kernel/drivers/media/platform/marvell-ccic/mcam-core.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include /* * Create our own symbols for the supported buffer modes, but, for now, @@ -146,7 +146,6 @@ struct mcam_camera { struct v4l2_ctrl_handler ctrl_handler; enum mcam_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ - int users; /* How many open FDs */ struct mcam_frame_state frame_state; /* Frame state counter */ /* @@ -163,6 +162,8 @@ struct mcam_camera { unsigned int nbufs; /* How many are alloc'd */ int next_buf; /* Next to consume (dev_lock) */ + char bus_info[32]; /* querycap bus_info */ + /* DMA buffers - vmalloc mode */ #ifdef MCAM_MODE_VMALLOC unsigned int dma_buf_size; /* allocated size */ diff --git a/kernel/drivers/media/platform/marvell-ccic/mmp-driver.c b/kernel/drivers/media/platform/marvell-ccic/mmp-driver.c index 0ed9b3adf..b5f165a68 100644 --- a/kernel/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/kernel/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -371,6 +371,7 @@ static int mmpcam_probe(struct platform_device *pdev) mcam->lane = pdata->lane; mcam->chip_id = MCAM_ARMADA610; mcam->buffer_mode = B_DMA_sg; + strlcpy(mcam->bus_info, "platform:mmp-camera", sizeof(mcam->bus_info)); spin_lock_init(&mcam->dev_lock); /* * Get our I/O memory. diff --git a/kernel/drivers/media/platform/mx2_emmaprp.c b/kernel/drivers/media/platform/mx2_emmaprp.c index 87314b743..03a1b6066 100644 --- a/kernel/drivers/media/platform/mx2_emmaprp.c +++ b/kernel/drivers/media/platform/mx2_emmaprp.c @@ -351,7 +351,7 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data) { struct emmaprp_dev *pcdev = data; struct emmaprp_ctx *curr_ctx; - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; unsigned long flags; u32 irqst; @@ -375,13 +375,13 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; - dst_vb->v4l2_buf.flags &= + dst_vb->timestamp = src_vb->timestamp; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.flags |= - src_vb->v4l2_buf.flags + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode; + dst_vb->timecode = src_vb->timecode; spin_lock_irqsave(&pcdev->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); @@ -689,7 +689,7 @@ static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { * Queue operations */ static int emmaprp_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -742,8 +742,9 @@ static int emmaprp_buf_prepare(struct vb2_buffer *vb) static void emmaprp_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } static struct vb2_ops emmaprp_qops = { diff --git a/kernel/drivers/media/platform/omap/Kconfig b/kernel/drivers/media/platform/omap/Kconfig index dc2aaab54..217d613b0 100644 --- a/kernel/drivers/media/platform/omap/Kconfig +++ b/kernel/drivers/media/platform/omap/Kconfig @@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB + select FRAME_VECTOR default n ---help--- V4L2 Display driver support for OMAP2/3 based boards. diff --git a/kernel/drivers/media/platform/omap/omap_vout.c b/kernel/drivers/media/platform/omap/omap_vout.c index 17b189a81..70c28d19e 100644 --- a/kernel/drivers/media/platform/omap/omap_vout.c +++ b/kernel/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, + u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, true, false, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -445,7 +433,7 @@ static int omapvid_init(struct omap_vout_device *vout, u32 addr) int ret = 0, i; struct v4l2_window *win; struct omap_overlay *ovl; - int posx, posy, outw, outh, temp; + int posx, posy, outw, outh; struct omap_video_timings *timing; struct omapvideo_info *ovid = &vout->vid_info; @@ -468,9 +456,7 @@ static int omapvid_init(struct omap_vout_device *vout, u32 addr) /* Invert the height and width for 90 * and 270 degree rotation */ - temp = outw; - outw = outh; - outh = temp; + swap(outw, outh); posy = (timing->y_res - win->w.width) - win->w.left; posx = win->w.top; break; @@ -481,9 +467,7 @@ static int omapvid_init(struct omap_vout_device *vout, u32 addr) break; case dss_rotation_270_degree: - temp = outw; - outw = outh; - outh = temp; + swap(outw, outh); posy = win->w.left; posx = (timing->x_res - win->w.height) - win->w.top; break; @@ -788,11 +772,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -838,12 +826,13 @@ static void omap_vout_buffer_queue(struct videobuf_queue *q, static void omap_vout_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct omap_vout_device *vout = q->priv_data; - vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_frames(vec); + frame_vector_destroy(vec); + } } /* @@ -876,7 +865,7 @@ static void omap_vout_vm_close(struct vm_area_struct *vma) vout->mmap_count--; } -static struct vm_operations_struct omap_vout_vm_ops = { +static const struct vm_operations_struct omap_vout_vm_ops = { .open = omap_vout_vm_open, .close = omap_vout_vm_close, }; diff --git a/kernel/drivers/media/platform/omap3isp/isp.c b/kernel/drivers/media/platform/omap3isp/isp.c index 947d8be7b..56e683b19 100644 --- a/kernel/drivers/media/platform/omap3isp/isp.c +++ b/kernel/drivers/media/platform/omap3isp/isp.c @@ -101,7 +101,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0000, /* csi2a, len 0x0170 */ 0x0170, /* csiphy2, len 0x000c */ }, - .syscon_offset = 0xdc, .phy_type = ISP_PHY_TYPE_3430, }, { @@ -124,7 +123,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0570, /* csiphy1, len 0x000c */ 0x05c0, /* csi2c, len 0x0040 (2nd area) */ }, - .syscon_offset = 0x2f0, .phy_type = ISP_PHY_TYPE_3630, }, }; @@ -1796,47 +1794,6 @@ static void isp_unregister_entities(struct isp_device *isp) media_device_unregister(&isp->media_dev); } -/* - * isp_register_subdev - Register a sub-device - * @isp: OMAP3 ISP device - * @isp_subdev: platform data related to a sub-device - * - * Register an I2C sub-device which has not been registered by other - * means (such as the Device Tree). - * - * Return a pointer to the sub-device if it has been successfully - * registered, or NULL otherwise. - */ -static struct v4l2_subdev * -isp_register_subdev(struct isp_device *isp, - struct isp_platform_subdev *isp_subdev) -{ - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - - if (isp_subdev->board_info == NULL) - return NULL; - - adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id); - if (adapter == NULL) { - dev_err(isp->dev, - "%s: Unable to get I2C adapter %d for device %s\n", - __func__, isp_subdev->i2c_adapter_id, - isp_subdev->board_info->type); - return NULL; - } - - sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, - isp_subdev->board_info, NULL); - if (sd == NULL) { - dev_err(isp->dev, "%s: Unable to register subdev %s\n", - __func__, isp_subdev->board_info->type); - return NULL; - } - - return sd; -} - static int isp_link_entity( struct isp_device *isp, struct media_entity *entity, enum isp_interface_type interface) @@ -1910,8 +1867,6 @@ static int isp_link_entity( static int isp_register_entities(struct isp_device *isp) { - struct isp_platform_data *pdata = isp->pdata; - struct isp_platform_subdev *isp_subdev; int ret; isp->media_dev.dev = isp->dev; @@ -1968,42 +1923,9 @@ static int isp_register_entities(struct isp_device *isp) if (ret < 0) goto done; - /* - * Device Tree --- the external sub-devices will be registered - * later. The same goes for the sub-device node registration. - */ - if (isp->dev->of_node) - return 0; - - /* Register external entities */ - for (isp_subdev = pdata ? pdata->subdevs : NULL; - isp_subdev && isp_subdev->board_info; isp_subdev++) { - struct v4l2_subdev *sd; - - sd = isp_register_subdev(isp, isp_subdev); - - /* - * No bus information --- this is either a flash or a - * lens subdev. - */ - if (!sd || !isp_subdev->bus) - continue; - - sd->host_priv = isp_subdev->bus; - - ret = isp_link_entity(isp, &sd->entity, - isp_subdev->bus->interface); - if (ret < 0) - goto done; - } - - ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); - done: - if (ret < 0) { + if (ret < 0) isp_unregister_entities(isp); - v4l2_async_notifier_unregister(&isp->notifier); - } return ret; } @@ -2404,37 +2326,24 @@ static int isp_probe(struct platform_device *pdev) return -ENOMEM; } - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", - &isp->phy_type); - if (ret) - return ret; + ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", + &isp->phy_type); + if (ret) + return ret; - isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "syscon"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); + isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + 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); - if (ret) - return ret; + 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); - if (ret < 0) - return ret; - ret = v4l2_async_notifier_register(&isp->v4l2_dev, - &isp->notifier); - if (ret) - return ret; - } else { - isp->pdata = pdev->dev.platform_data; - isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); - dev_warn(&pdev->dev, - "Platform data support is deprecated! Please move to DT now!\n"); - } + ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + if (ret < 0) + return ret; isp->autoidle = autoidle; @@ -2513,11 +2422,6 @@ static int isp_probe(struct platform_device *pdev) goto error_isp; } - if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) { - isp->syscon_offset = isp_res_maps[m].syscon_offset; - isp->phy_type = isp_res_maps[m].phy_type; - } - for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) isp->mmio_base[i] = isp->mmio_base[0] + isp_res_maps[m].offset[i]; @@ -2557,18 +2461,24 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_iommu; - isp->notifier.bound = isp_subdev_notifier_bound; - isp->notifier.complete = isp_subdev_notifier_complete; - ret = isp_register_entities(isp); if (ret < 0) goto error_modules; + isp->notifier.bound = isp_subdev_notifier_bound; + isp->notifier.complete = isp_subdev_notifier_complete; + + ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier); + if (ret) + goto error_register_entities; + isp_core_init(isp, 1); omap3isp_put(isp); return 0; +error_register_entities: + isp_unregister_entities(isp); error_modules: isp_cleanup_modules(isp); error_iommu: diff --git a/kernel/drivers/media/platform/omap3isp/isp.h b/kernel/drivers/media/platform/omap3isp/isp.h index e57994317..5acc2e651 100644 --- a/kernel/drivers/media/platform/omap3isp/isp.h +++ b/kernel/drivers/media/platform/omap3isp/isp.h @@ -17,7 +17,6 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include "omap3isp.h" #include "ispstat.h" #include "ispccdc.h" #include "ispreg.h" @@ -101,15 +101,11 @@ struct regmap; * struct isp_res_mapping - Map ISP io resources to ISP revision. * @isp_rev: ISP_REVISION_x_x * @offset: register offsets of various ISP sub-blocks - * @syscon_offset: offset of the syscon register for 343x / 3630 - * (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively) - * from the syscon base address * @phy_type: ISP_PHY_TYPE_{3430,3630} */ struct isp_res_mapping { u32 isp_rev; u32 offset[OMAP3_ISP_IOMEM_LAST]; - u32 syscon_offset; u32 phy_type; }; @@ -184,7 +180,6 @@ struct isp_device { u32 revision; /* platform HW resources */ - struct isp_platform_data *pdata; unsigned int irq_num; void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; diff --git a/kernel/drivers/media/platform/omap3isp/ispcsiphy.h b/kernel/drivers/media/platform/omap3isp/ispcsiphy.h index e17c88bea..28b63b28f 100644 --- a/kernel/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/kernel/drivers/media/platform/omap3isp/ispcsiphy.h @@ -17,7 +17,7 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H -#include +#include "omap3isp.h" struct isp_csi2_device; struct regulator; diff --git a/kernel/drivers/media/platform/omap3isp/isppreview.c b/kernel/drivers/media/platform/omap3isp/isppreview.c index 15cb254cc..13803270d 100644 --- a/kernel/drivers/media/platform/omap3isp/isppreview.c +++ b/kernel/drivers/media/platform/omap3isp/isppreview.c @@ -929,14 +929,10 @@ static void preview_setup_hw(struct isp_prev_device *prev, u32 update, u32 active) { unsigned int i; - u32 features; if (update == 0) return; - features = (prev->params.params[0].features & active) - | (prev->params.params[1].features & ~active); - for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { const struct preview_update *attr = &update_attrs[i]; struct prev_params *params; diff --git a/kernel/drivers/media/platform/omap3isp/ispstat.c b/kernel/drivers/media/platform/omap3isp/ispstat.c index 20434e83e..94d4c295d 100644 --- a/kernel/drivers/media/platform/omap3isp/ispstat.c +++ b/kernel/drivers/media/platform/omap3isp/ispstat.c @@ -235,7 +235,7 @@ static int isp_stat_buf_queue(struct ispstat *stat) if (!stat->active_buf) return STAT_NO_BUF; - ktime_get_ts(&stat->active_buf->ts); + v4l2_get_timestamp(&stat->active_buf->ts); stat->active_buf->buf_size = stat->buf_size; if (isp_stat_buf_check_magic(stat, stat->active_buf)) { @@ -496,8 +496,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, return PTR_ERR(buf); } - data->ts.tv_sec = buf->ts.tv_sec; - data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC; + data->ts = buf->ts; data->config_counter = buf->config_counter; data->frame_number = buf->frame_number; data->buf_size = buf->buf_size; diff --git a/kernel/drivers/media/platform/omap3isp/ispstat.h b/kernel/drivers/media/platform/omap3isp/ispstat.h index b79380d83..6d9b0244f 100644 --- a/kernel/drivers/media/platform/omap3isp/ispstat.h +++ b/kernel/drivers/media/platform/omap3isp/ispstat.h @@ -39,7 +39,7 @@ struct ispstat_buffer { struct sg_table sgt; void *virt_addr; dma_addr_t dma_addr; - struct timespec ts; + struct timeval ts; u32 buf_size; u32 frame_number; u16 config_counter; diff --git a/kernel/drivers/media/platform/omap3isp/ispvideo.c b/kernel/drivers/media/platform/omap3isp/ispvideo.c index d285af18d..f4f591652 100644 --- a/kernel/drivers/media/platform/omap3isp/ispvideo.c +++ b/kernel/drivers/media/platform/omap3isp/ispvideo.c @@ -320,7 +320,7 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) */ static int isp_video_queue_setup(struct vb2_queue *queue, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -342,8 +342,9 @@ static int isp_video_queue_setup(struct vb2_queue *queue, static int isp_video_buffer_prepare(struct vb2_buffer *buf) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; dma_addr_t addr; @@ -363,7 +364,8 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) return -EINVAL; } - vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage); + vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, + vfh->format.fmt.pix.sizeimage); buffer->dma = addr; return 0; @@ -380,8 +382,9 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) */ static void isp_video_buffer_queue(struct vb2_buffer *buf) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); enum isp_pipeline_state state; @@ -392,7 +395,7 @@ static void isp_video_buffer_queue(struct vb2_buffer *buf) spin_lock_irqsave(&video->irqlock, flags); if (unlikely(video->error)) { - vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&video->irqlock, flags); return; } @@ -464,7 +467,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) list_del(&buf->irqlist); spin_unlock_irqrestore(&video->irqlock, flags); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.timestamp); /* Do frame number propagation only if this is the output video node. * Frame number either comes from the CSI receivers or it gets @@ -473,15 +476,15 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) * first, so the input number might lag behind by 1 in some cases. */ if (video == pipe->output && !pipe->do_propagation) - buf->vb.v4l2_buf.sequence = + buf->vb.sequence = atomic_inc_return(&pipe->frame_number); else - buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); + buf->vb.sequence = atomic_read(&pipe->frame_number); if (pipe->field != V4L2_FIELD_NONE) - buf->vb.v4l2_buf.sequence /= 2; + buf->vb.sequence /= 2; - buf->vb.v4l2_buf.field = pipe->field; + buf->vb.field = pipe->field; /* Report pipeline errors to userspace on the capture device side. */ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { @@ -491,7 +494,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) state = VB2_BUF_STATE_DONE; } - vb2_buffer_done(&buf->vb, state); + vb2_buffer_done(&buf->vb.vb2_buf, state); spin_lock_irqsave(&video->irqlock, flags); @@ -546,7 +549,7 @@ void omap3isp_video_cancel_stream(struct isp_video *video) buf = list_first_entry(&video->dmaqueue, struct isp_buffer, irqlist); list_del(&buf->irqlist); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } video->error = true; @@ -1018,8 +1021,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->entities = 0; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); + /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; @@ -1100,8 +1102,7 @@ err_set_stream: err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ /* The DMA queue must be emptied here, otherwise CCDC interrupts that * will get triggered the next time the CCDC is powered up will try to * access buffers that might have been freed but still present in the @@ -1161,8 +1162,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = NULL; video->error = false; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); done: diff --git a/kernel/drivers/media/platform/omap3isp/ispvideo.h b/kernel/drivers/media/platform/omap3isp/ispvideo.h index 4071dd706..bcf0e0acc 100644 --- a/kernel/drivers/media/platform/omap3isp/ispvideo.h +++ b/kernel/drivers/media/platform/omap3isp/ispvideo.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #define ISP_VIDEO_DRIVER_NAME "ispvideo" #define ISP_VIDEO_DRIVER_VERSION "0.0.2" @@ -122,7 +122,7 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe) * @dma: DMA address */ struct isp_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head irqlist; dma_addr_t dma; }; diff --git a/kernel/drivers/media/platform/omap3isp/omap3isp.h b/kernel/drivers/media/platform/omap3isp/omap3isp.h new file mode 100644 index 000000000..190e259a6 --- /dev/null +++ b/kernel/drivers/media/platform/omap3isp/omap3isp.h @@ -0,0 +1,132 @@ +/* + * omap3isp.h + * + * TI OMAP3 ISP - Bus Configuration + * + * Copyright (C) 2011 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __OMAP3ISP_H__ +#define __OMAP3ISP_H__ + +enum isp_interface_type { + ISP_INTERFACE_PARALLEL, + ISP_INTERFACE_CSI2A_PHY2, + ISP_INTERFACE_CCP2B_PHY1, + ISP_INTERFACE_CCP2B_PHY2, + ISP_INTERFACE_CSI2C_PHY1, +}; + +/** + * struct isp_parallel_cfg - Parallel interface configuration + * @data_lane_shift: Data lane shifter + * 0 - CAMEXT[13:0] -> CAM[13:0] + * 1 - CAMEXT[13:2] -> CAM[11:0] + * 2 - CAMEXT[13:4] -> CAM[9:0] + * 3 - CAMEXT[13:6] -> CAM[7:0] + * @clk_pol: Pixel clock polarity + * 0 - Sample on rising edge, 1 - Sample on falling edge + * @hs_pol: Horizontal synchronization polarity + * 0 - Active high, 1 - Active low + * @vs_pol: Vertical synchronization polarity + * 0 - Active high, 1 - Active low + * @fld_pol: Field signal polarity + * 0 - Positive, 1 - Negative + * @data_pol: Data polarity + * 0 - Normal, 1 - One's complement + */ +struct isp_parallel_cfg { + unsigned int data_lane_shift:2; + unsigned int clk_pol:1; + unsigned int hs_pol:1; + unsigned int vs_pol:1; + unsigned int fld_pol:1; + unsigned int data_pol:1; +}; + +enum { + ISP_CCP2_PHY_DATA_CLOCK = 0, + ISP_CCP2_PHY_DATA_STROBE = 1, +}; + +enum { + ISP_CCP2_MODE_MIPI = 0, + ISP_CCP2_MODE_CCP2 = 1, +}; + +/** + * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct isp_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY1_NUM_DATA_LANES 1 +#define ISP_CSIPHY2_NUM_DATA_LANES 2 + +/** + * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct isp_csiphy_lanes_cfg { + struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct isp_csiphy_lane clk; +}; + +/** + * struct isp_ccp2_cfg - CCP2 interface configuration + * @strobe_clk_pol: Strobe/clock polarity + * 0 - Non Inverted, 1 - Inverted + * @crc: Enable the cyclic redundancy check + * @ccp2_mode: Enable CCP2 compatibility mode + * ISP_CCP2_MODE_MIPI - MIPI-CSI1 mode + * ISP_CCP2_MODE_CCP2 - CCP2 mode + * @phy_layer: Physical layer selection + * ISP_CCP2_PHY_DATA_CLOCK - Data/clock physical layer + * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer + * @vpclk_div: Video port output clock control + */ +struct isp_ccp2_cfg { + unsigned int strobe_clk_pol:1; + unsigned int crc:1; + unsigned int ccp2_mode:1; + unsigned int phy_layer:1; + unsigned int vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +/** + * struct isp_csi2_cfg - CSI2 interface configuration + * @crc: Enable the cyclic redundancy check + */ +struct isp_csi2_cfg { + unsigned crc:1; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +struct isp_bus_cfg { + enum isp_interface_type interface; + union { + struct isp_parallel_cfg parallel; + struct isp_ccp2_cfg ccp2; + struct isp_csi2_cfg csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +#endif /* __OMAP3ISP_H__ */ diff --git a/kernel/drivers/media/platform/rcar_jpu.c b/kernel/drivers/media/platform/rcar_jpu.c new file mode 100644 index 000000000..f8e3e83c5 --- /dev/null +++ b/kernel/drivers/media/platform/rcar_jpu.c @@ -0,0 +1,1802 @@ +/* + * Author: Mikhail Ulyanov + * Copyright (C) 2014-2015 Cogent Embedded, Inc. + * Copyright (C) 2014-2015 Renesas Electronics Corporation + * + * This is based on the drivers/media/platform/s5p-jpeg driver by + * Andrzej Pietrasiewicz and Jacek Anaszewski. + * Some portions of code inspired by VSP1 driver by Laurent Pinchart. + * + * TODO in order of priority: + * 1) Rotation + * 2) Cropping + * 3) V4L2_CID_JPEG_ACTIVE_MARKER + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRV_NAME "rcar_jpu" + +/* + * Align JPEG header end to cache line to make sure we will not have any issues + * with cache; additionally to requerment (33.3.27 R01UH0501EJ0100 Rev.1.00) + */ +#define JPU_JPEG_HDR_SIZE (ALIGN(0x258, L1_CACHE_BYTES)) +#define JPU_JPEG_MAX_BYTES_PER_PIXEL 2 /* 16 bit precision format */ +#define JPU_JPEG_MIN_SIZE 25 /* SOI + SOF + EOI */ +#define JPU_JPEG_QTBL_SIZE 0x40 +#define JPU_JPEG_HDCTBL_SIZE 0x1c +#define JPU_JPEG_HACTBL_SIZE 0xb2 +#define JPU_JPEG_HEIGHT_OFFSET 0x91 +#define JPU_JPEG_WIDTH_OFFSET 0x93 +#define JPU_JPEG_SUBS_OFFSET 0x97 +#define JPU_JPEG_QTBL_LUM_OFFSET 0x07 +#define JPU_JPEG_QTBL_CHR_OFFSET 0x4c +#define JPU_JPEG_HDCTBL_LUM_OFFSET 0xa4 +#define JPU_JPEG_HACTBL_LUM_OFFSET 0xc5 +#define JPU_JPEG_HDCTBL_CHR_OFFSET 0x17c +#define JPU_JPEG_HACTBL_CHR_OFFSET 0x19d +#define JPU_JPEG_PADDING_OFFSET 0x24f +#define JPU_JPEG_LUM 0x00 +#define JPU_JPEG_CHR 0x01 +#define JPU_JPEG_DC 0x00 +#define JPU_JPEG_AC 0x10 + +#define JPU_JPEG_422 0x21 +#define JPU_JPEG_420 0x22 + +#define JPU_JPEG_DEFAULT_422_PIX_FMT V4L2_PIX_FMT_NV16M +#define JPU_JPEG_DEFAULT_420_PIX_FMT V4L2_PIX_FMT_NV12M + +/* JPEG markers */ +#define TEM 0x01 +#define SOF0 0xc0 +#define RST 0xd0 +#define SOI 0xd8 +#define EOI 0xd9 +#define DHP 0xde +#define DHT 0xc4 +#define COM 0xfe +#define DQT 0xdb +#define DRI 0xdd +#define APP0 0xe0 + +#define JPU_RESET_TIMEOUT 100 /* ms */ +#define JPU_JOB_TIMEOUT 300 /* ms */ +#define JPU_MAX_QUALITY 4 +#define JPU_WIDTH_MIN 16 +#define JPU_HEIGHT_MIN 16 +#define JPU_WIDTH_MAX 4096 +#define JPU_HEIGHT_MAX 4096 +#define JPU_MEMALIGN 8 + +/* Flags that indicate a format can be used for capture/output */ +#define JPU_FMT_TYPE_OUTPUT 0 +#define JPU_FMT_TYPE_CAPTURE 1 +#define JPU_ENC_CAPTURE (1 << 0) +#define JPU_ENC_OUTPUT (1 << 1) +#define JPU_DEC_CAPTURE (1 << 2) +#define JPU_DEC_OUTPUT (1 << 3) + +/* + * JPEG registers and bits + */ + +/* JPEG code mode register */ +#define JCMOD 0x00 +#define JCMOD_PCTR (1 << 7) +#define JCMOD_MSKIP_ENABLE (1 << 5) +#define JCMOD_DSP_ENC (0 << 3) +#define JCMOD_DSP_DEC (1 << 3) +#define JCMOD_REDU (7 << 0) +#define JCMOD_REDU_422 (1 << 0) +#define JCMOD_REDU_420 (2 << 0) + +/* JPEG code command register */ +#define JCCMD 0x04 +#define JCCMD_SRST (1 << 12) +#define JCCMD_JEND (1 << 2) +#define JCCMD_JSRT (1 << 0) + +/* JPEG code quantanization table number register */ +#define JCQTN 0x0c +#define JCQTN_SHIFT(t) (((t) - 1) << 1) + +/* JPEG code Huffman table number register */ +#define JCHTN 0x10 +#define JCHTN_AC_SHIFT(t) (((t) << 1) - 1) +#define JCHTN_DC_SHIFT(t) (((t) - 1) << 1) + +#define JCVSZU 0x1c /* JPEG code vertical size upper register */ +#define JCVSZD 0x20 /* JPEG code vertical size lower register */ +#define JCHSZU 0x24 /* JPEG code horizontal size upper register */ +#define JCHSZD 0x28 /* JPEG code horizontal size lower register */ +#define JCSZ_MASK 0xff /* JPEG code h/v size register contains only 1 byte*/ + +#define JCDTCU 0x2c /* JPEG code data count upper register */ +#define JCDTCM 0x30 /* JPEG code data count middle register */ +#define JCDTCD 0x34 /* JPEG code data count lower register */ + +/* JPEG interrupt enable register */ +#define JINTE 0x38 +#define JINTE_ERR (7 << 5) /* INT5 + INT6 + INT7 */ +#define JINTE_TRANSF_COMPL (1 << 10) + +/* JPEG interrupt status register */ +#define JINTS 0x3c +#define JINTS_MASK 0x7c68 +#define JINTS_ERR (1 << 5) +#define JINTS_PROCESS_COMPL (1 << 6) +#define JINTS_TRANSF_COMPL (1 << 10) + +#define JCDERR 0x40 /* JPEG code decode error register */ +#define JCDERR_MASK 0xf /* JPEG code decode error register mask*/ + +/* JPEG interface encoding */ +#define JIFECNT 0x70 +#define JIFECNT_INFT_422 0 +#define JIFECNT_INFT_420 1 +#define JIFECNT_SWAP_WB (3 << 4) /* to JPU */ + +#define JIFESYA1 0x74 /* encode source Y address register 1 */ +#define JIFESCA1 0x78 /* encode source C address register 1 */ +#define JIFESYA2 0x7c /* encode source Y address register 2 */ +#define JIFESCA2 0x80 /* encode source C address register 2 */ +#define JIFESMW 0x84 /* encode source memory width register */ +#define JIFESVSZ 0x88 /* encode source vertical size register */ +#define JIFESHSZ 0x8c /* encode source horizontal size register */ +#define JIFEDA1 0x90 /* encode destination address register 1 */ +#define JIFEDA2 0x94 /* encode destination address register 2 */ + +/* JPEG decoding control register */ +#define JIFDCNT 0xa0 +#define JIFDCNT_SWAP_WB (3 << 1) /* from JPU */ + +#define JIFDSA1 0xa4 /* decode source address register 1 */ +#define JIFDDMW 0xb0 /* decode destination memory width register */ +#define JIFDDVSZ 0xb4 /* decode destination vert. size register */ +#define JIFDDHSZ 0xb8 /* decode destination horiz. size register */ +#define JIFDDYA1 0xbc /* decode destination Y address register 1 */ +#define JIFDDCA1 0xc0 /* decode destination C address register 1 */ + +#define JCQTBL(n) (0x10000 + (n) * 0x40) /* quantization tables regs */ +#define JCHTBD(n) (0x10100 + (n) * 0x100) /* Huffman table DC regs */ +#define JCHTBA(n) (0x10120 + (n) * 0x100) /* Huffman table AC regs */ + +/** + * struct jpu - JPEG IP abstraction + * @mutex: the mutex protecting this structure + * @lock: spinlock protecting the device contexts + * @v4l2_dev: v4l2 device for mem2mem mode + * @vfd_encoder: video device node for encoder mem2mem mode + * @vfd_decoder: video device node for decoder mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @curr: pointer to current context + * @irq_queue: interrupt handler waitqueue + * @regs: JPEG IP registers mapping + * @irq: JPEG IP irq + * @clk: JPEG IP clock + * @dev: JPEG IP struct device + * @alloc_ctx: videobuf2 memory allocator's context + * @ref_count: reference counter + */ +struct jpu { + struct mutex mutex; + spinlock_t lock; + struct v4l2_device v4l2_dev; + struct video_device vfd_encoder; + struct video_device vfd_decoder; + struct v4l2_m2m_dev *m2m_dev; + struct jpu_ctx *curr; + wait_queue_head_t irq_queue; + + void __iomem *regs; + unsigned int irq; + struct clk *clk; + struct device *dev; + void *alloc_ctx; + int ref_count; +}; + +/** + * struct jpu_buffer - driver's specific video buffer + * @buf: m2m buffer + * @compr_quality: destination image quality in compression mode + * @subsampling: source image subsampling in decompression mode + */ +struct jpu_buffer { + struct v4l2_m2m_buffer buf; + unsigned short compr_quality; + unsigned char subsampling; +}; + +/** + * struct jpu_fmt - driver's internal format data + * @fourcc: the fourcc code, 0 if not applicable + * @colorspace: the colorspace specifier + * @bpp: number of bits per pixel per plane + * @h_align: horizontal alignment order (align to 2^h_align) + * @v_align: vertical alignment order (align to 2^v_align) + * @subsampling: (horizontal:4 | vertical:4) subsampling factor + * @num_planes: number of planes + * @types: types of queue this format is applicable to + */ +struct jpu_fmt { + u32 fourcc; + u32 colorspace; + u8 bpp[2]; + u8 h_align; + u8 v_align; + u8 subsampling; + u8 num_planes; + u16 types; +}; + +/** + * jpu_q_data - parameters of one queue + * @fmtinfo: driver-specific format of this queue + * @format: multiplanar format of this queue + * @sequence: sequence number + */ +struct jpu_q_data { + struct jpu_fmt *fmtinfo; + struct v4l2_pix_format_mplane format; + unsigned int sequence; +}; + +/** + * jpu_ctx - the device context data + * @jpu: JPEG IP device for this context + * @encoder: compression (encode) operation or decompression (decode) + * @compr_quality: destination image quality in compression (encode) mode + * @out_q: source (output) queue information + * @cap_q: destination (capture) queue information + * @fh: file handler + * @ctrl_handler: controls handler + */ +struct jpu_ctx { + struct jpu *jpu; + bool encoder; + unsigned short compr_quality; + struct jpu_q_data out_q; + struct jpu_q_data cap_q; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; +}; + + /** + * jpeg_buffer - description of memory containing input JPEG data + * @end: end position in the buffer + * @curr: current position in the buffer + */ +struct jpeg_buffer { + void *end; + void *curr; +}; + +static struct jpu_fmt jpu_formats[] = { + { V4L2_PIX_FMT_JPEG, V4L2_COLORSPACE_JPEG, + {0, 0}, 0, 0, 0, 1, JPU_ENC_CAPTURE | JPU_DEC_OUTPUT }, + { V4L2_PIX_FMT_NV16M, V4L2_COLORSPACE_SRGB, + {8, 8}, 2, 2, JPU_JPEG_422, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV12M, V4L2_COLORSPACE_SRGB, + {8, 4}, 2, 2, JPU_JPEG_420, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV16, V4L2_COLORSPACE_SRGB, + {16, 0}, 2, 2, JPU_JPEG_422, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV12, V4L2_COLORSPACE_SRGB, + {12, 0}, 2, 2, JPU_JPEG_420, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, +}; + +static const u8 zigzag[] = { + 0x03, 0x02, 0x0b, 0x13, 0x0a, 0x01, 0x00, 0x09, + 0x12, 0x1b, 0x23, 0x1a, 0x11, 0x08, 0x07, 0x06, + 0x0f, 0x10, 0x19, 0x22, 0x2b, 0x33, 0x2a, 0x21, + 0x18, 0x17, 0x0e, 0x05, 0x04, 0x0d, 0x16, 0x1f, + 0x20, 0x29, 0x32, 0x3b, 0x3a, 0x31, 0x28, 0x27, + 0x1e, 0x15, 0x0e, 0x14, 0x10, 0x26, 0x2f, 0x30, + 0x39, 0x38, 0x37, 0x2e, 0x25, 0x1c, 0x24, 0x2b, + 0x36, 0x3f, 0x3e, 0x35, 0x2c, 0x34, 0x3d, 0x3c +}; + +#define QTBL_SIZE (ALIGN(JPU_JPEG_QTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +#define HDCTBL_SIZE (ALIGN(JPU_JPEG_HDCTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +#define HACTBL_SIZE (ALIGN(JPU_JPEG_HACTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +/* + * Start of image; Quantization tables + * SOF0 (17 bytes payload) is Baseline DCT - Sample precision, height, width, + * Number of image components, (Ci:8 - Hi:4 - Vi:4 - Tq:8) * 3 - Y,Cb,Cr; + * Huffman tables; Padding with 0xff (33.3.27 R01UH0501EJ0100 Rev.1.00) + */ +#define JPU_JPEG_HDR_BLOB { \ + 0xff, SOI, 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_LUM, \ + [JPU_JPEG_QTBL_LUM_OFFSET ... \ + JPU_JPEG_QTBL_LUM_OFFSET + JPU_JPEG_QTBL_SIZE - 1] = 0x00, \ + 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_CHR, \ + [JPU_JPEG_QTBL_CHR_OFFSET ... JPU_JPEG_QTBL_CHR_OFFSET + \ + JPU_JPEG_QTBL_SIZE - 1] = 0x00, 0xff, SOF0, 0x00, 0x11, 0x08, \ + [JPU_JPEG_HEIGHT_OFFSET ... JPU_JPEG_HEIGHT_OFFSET + 1] = 0x00, \ + [JPU_JPEG_WIDTH_OFFSET ... JPU_JPEG_WIDTH_OFFSET + 1] = 0x00, \ + 0x03, 0x01, [JPU_JPEG_SUBS_OFFSET] = 0x00, JPU_JPEG_LUM, \ + 0x02, 0x11, JPU_JPEG_CHR, 0x03, 0x11, JPU_JPEG_CHR, \ + 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_DC, \ + [JPU_JPEG_HDCTBL_LUM_OFFSET ... \ + JPU_JPEG_HDCTBL_LUM_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_AC, \ + [JPU_JPEG_HACTBL_LUM_OFFSET ... \ + JPU_JPEG_HACTBL_LUM_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_DC, \ + [JPU_JPEG_HDCTBL_CHR_OFFSET ... \ + JPU_JPEG_HDCTBL_CHR_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_AC, \ + [JPU_JPEG_HACTBL_CHR_OFFSET ... \ + JPU_JPEG_HACTBL_CHR_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ + [JPU_JPEG_PADDING_OFFSET ... JPU_JPEG_HDR_SIZE - 1] = 0xff \ +} + +static unsigned char jpeg_hdrs[JPU_MAX_QUALITY][JPU_JPEG_HDR_SIZE] = { + [0 ... JPU_MAX_QUALITY - 1] = JPU_JPEG_HDR_BLOB +}; + +static const unsigned int qtbl_lum[JPU_MAX_QUALITY][QTBL_SIZE] = { + { + 0x14101927, 0x322e3e44, 0x10121726, 0x26354144, + 0x19171f26, 0x35414444, 0x27262635, 0x41444444, + 0x32263541, 0x44444444, 0x2e354144, 0x44444444, + 0x3e414444, 0x44444444, 0x44444444, 0x44444444 + }, + { + 0x100b0b10, 0x171b1f1e, 0x0b0c0c0f, 0x1417171e, + 0x0b0c0d10, 0x171a232f, 0x100f1017, 0x1a252f40, + 0x1714171a, 0x27334040, 0x1b171a25, 0x33404040, + 0x1f17232f, 0x40404040, 0x1e1e2f40, 0x40404040 + }, + { + 0x0c08080c, 0x11151817, 0x0809090b, 0x0f131217, + 0x08090a0c, 0x13141b24, 0x0c0b0c15, 0x141c2435, + 0x110f1314, 0x1e27333b, 0x1513141c, 0x27333b3b, + 0x18121b24, 0x333b3b3b, 0x17172435, 0x3b3b3b3b + }, + { + 0x08060608, 0x0c0e1011, 0x06060608, 0x0a0d0c0f, + 0x06060708, 0x0d0e1218, 0x0808080e, 0x0d131823, + 0x0c0a0d0d, 0x141a2227, 0x0e0d0e13, 0x1a222727, + 0x100c1318, 0x22272727, 0x110f1823, 0x27272727 + } +}; + +static const unsigned int qtbl_chr[JPU_MAX_QUALITY][QTBL_SIZE] = { + { + 0x15192026, 0x36444444, 0x191c1826, 0x36444444, + 0x2018202b, 0x42444444, 0x26262b35, 0x44444444, + 0x36424444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444 + }, + { + 0x110f1115, 0x141a2630, 0x0f131211, 0x141a232b, + 0x11121416, 0x1a1e2e35, 0x1511161c, 0x1e273540, + 0x14141a1e, 0x27304040, 0x1a1a1e27, 0x303f4040, + 0x26232e35, 0x40404040, 0x302b3540, 0x40404040 + }, + { + 0x0d0b0d10, 0x14141d25, 0x0b0e0e0e, 0x10141a20, + 0x0d0e0f11, 0x14172328, 0x100e1115, 0x171e2832, + 0x14101417, 0x1e25323b, 0x1414171e, 0x25303b3b, + 0x1d1a2328, 0x323b3b3b, 0x25202832, 0x3b3b3b3b + }, + { + 0x0908090b, 0x0e111318, 0x080a090b, 0x0e0d1116, + 0x09090d0e, 0x0d0f171a, 0x0b0b0e0e, 0x0f141a21, + 0x0e0e0d0f, 0x14182127, 0x110d0f14, 0x18202727, + 0x1311171a, 0x21272727, 0x18161a21, 0x27272727 + } +}; + +static const unsigned int hdctbl_lum[HDCTBL_SIZE] = { + 0x00010501, 0x01010101, 0x01000000, 0x00000000, + 0x00010203, 0x04050607, 0x08090a0b +}; + +static const unsigned int hdctbl_chr[HDCTBL_SIZE] = { + 0x00010501, 0x01010101, 0x01000000, 0x00000000, + 0x00010203, 0x04050607, 0x08090a0b +}; + +static const unsigned int hactbl_lum[HACTBL_SIZE] = { + 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, + 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, + 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, + 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, + 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, + 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, + 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, + 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 +}; + +static const unsigned int hactbl_chr[HACTBL_SIZE] = { + 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, + 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, + 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, + 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, + 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, + 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, + 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, + 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 +}; + +static const char *error_to_text[16] = { + "Normal", + "SOI not detected", + "SOF1 to SOFF detected", + "Subsampling not detected", + "SOF accuracy error", + "DQT accuracy error", + "Component error 1", + "Component error 2", + "SOF0, DQT, and DHT not detected when SOS detected", + "SOS not detected", + "EOI not detected", + "Restart interval data number error detected", + "Image size error", + "Last MCU data number error", + "Block data number error", + "Unknown" +}; + +static struct jpu_buffer *vb2_to_jpu_buffer(struct vb2_v4l2_buffer *vb) +{ + struct v4l2_m2m_buffer *b = + container_of(vb, struct v4l2_m2m_buffer, vb); + + return container_of(b, struct jpu_buffer, buf); +} + +static u32 jpu_read(struct jpu *jpu, unsigned int reg) +{ + return ioread32(jpu->regs + reg); +} + +static void jpu_write(struct jpu *jpu, u32 val, unsigned int reg) +{ + iowrite32(val, jpu->regs + reg); +} + +static struct jpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) +{ + return container_of(c->handler, struct jpu_ctx, ctrl_handler); +} + +static struct jpu_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct jpu_ctx, fh); +} + +static void jpu_set_tbl(struct jpu *jpu, u32 reg, const unsigned int *tbl, + unsigned int len) { + unsigned int i; + + for (i = 0; i < len; i++) + jpu_write(jpu, tbl[i], reg + (i << 2)); +} + +static void jpu_set_qtbl(struct jpu *jpu, unsigned short quality) +{ + jpu_set_tbl(jpu, JCQTBL(0), qtbl_lum[quality], QTBL_SIZE); + jpu_set_tbl(jpu, JCQTBL(1), qtbl_chr[quality], QTBL_SIZE); +} + +static void jpu_set_htbl(struct jpu *jpu) +{ + jpu_set_tbl(jpu, JCHTBD(0), hdctbl_lum, HDCTBL_SIZE); + jpu_set_tbl(jpu, JCHTBA(0), hactbl_lum, HACTBL_SIZE); + jpu_set_tbl(jpu, JCHTBD(1), hdctbl_chr, HDCTBL_SIZE); + jpu_set_tbl(jpu, JCHTBA(1), hactbl_chr, HACTBL_SIZE); +} + +static int jpu_wait_reset(struct jpu *jpu) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(JPU_RESET_TIMEOUT); + + while (jpu_read(jpu, JCCMD) & JCCMD_SRST) { + if (time_after(jiffies, timeout)) { + dev_err(jpu->dev, "timed out in reset\n"); + return -ETIMEDOUT; + } + schedule(); + } + + return 0; +} + +static int jpu_reset(struct jpu *jpu) +{ + jpu_write(jpu, JCCMD_SRST, JCCMD); + return jpu_wait_reset(jpu); +} + +/* + * ============================================================================ + * video ioctl operations + * ============================================================================ + */ +static void put_qtbl(u8 *p, const u8 *qtbl) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(zigzag); i++) + p[i] = *(qtbl + zigzag[i]); +} + +static void put_htbl(u8 *p, const u8 *htbl, unsigned int len) +{ + unsigned int i, j; + + for (i = 0; i < len; i += 4) + for (j = 0; j < 4 && (i + j) < len; ++j) + p[i + j] = htbl[i + 3 - j]; +} + +static void jpu_generate_hdr(unsigned short quality, unsigned char *p) +{ + put_qtbl(p + JPU_JPEG_QTBL_LUM_OFFSET, (const u8 *)qtbl_lum[quality]); + put_qtbl(p + JPU_JPEG_QTBL_CHR_OFFSET, (const u8 *)qtbl_chr[quality]); + + put_htbl(p + JPU_JPEG_HDCTBL_LUM_OFFSET, (const u8 *)hdctbl_lum, + JPU_JPEG_HDCTBL_SIZE); + put_htbl(p + JPU_JPEG_HACTBL_LUM_OFFSET, (const u8 *)hactbl_lum, + JPU_JPEG_HACTBL_SIZE); + + put_htbl(p + JPU_JPEG_HDCTBL_CHR_OFFSET, (const u8 *)hdctbl_chr, + JPU_JPEG_HDCTBL_SIZE); + put_htbl(p + JPU_JPEG_HACTBL_CHR_OFFSET, (const u8 *)hactbl_chr, + JPU_JPEG_HACTBL_SIZE); +} + +static int get_byte(struct jpeg_buffer *buf) +{ + if (buf->curr >= buf->end) + return -1; + + return *(u8 *)buf->curr++; +} + +static int get_word_be(struct jpeg_buffer *buf, unsigned int *word) +{ + if (buf->end - buf->curr < 2) + return -1; + + *word = get_unaligned_be16(buf->curr); + buf->curr += 2; + + return 0; +} + +static void skip(struct jpeg_buffer *buf, unsigned long len) +{ + buf->curr += min((unsigned long)(buf->end - buf->curr), len); +} + +static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width, + unsigned int *height) +{ + struct jpeg_buffer jpeg_buffer; + unsigned int word; + bool soi = false; + + jpeg_buffer.end = buffer + size; + jpeg_buffer.curr = buffer; + + /* + * basic size check and EOI - we don't want to let JPU cross + * buffer bounds in any case. Hope it's stopping by EOI. + */ + if (size < JPU_JPEG_MIN_SIZE || *(u8 *)(buffer + size - 1) != EOI) + return 0; + + for (;;) { + int c; + + /* skip preceding filler bytes */ + do + c = get_byte(&jpeg_buffer); + while (c == 0xff || c == 0); + + if (!soi && c == SOI) { + soi = true; + continue; + } else if (soi != (c != SOI)) + return 0; + + switch (c) { + case SOF0: /* SOF0: baseline JPEG */ + skip(&jpeg_buffer, 3); /* segment length and bpp */ + if (get_word_be(&jpeg_buffer, height) || + get_word_be(&jpeg_buffer, width) || + get_byte(&jpeg_buffer) != 3) /* YCbCr only */ + return 0; + + skip(&jpeg_buffer, 1); + return get_byte(&jpeg_buffer); + case DHT: + case DQT: + case COM: + case DRI: + case APP0 ... APP0 + 0x0f: + if (get_word_be(&jpeg_buffer, &word)) + return 0; + skip(&jpeg_buffer, (long)word - 2); + case 0: + break; + default: + return 0; + } + } + + return 0; +} + +static int jpu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (ctx->encoder) + strlcpy(cap->card, DRV_NAME " encoder", sizeof(cap->card)); + else + strlcpy(cap->card, DRV_NAME " decoder", sizeof(cap->card)); + + strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->jpu->dev)); + cap->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps; + memset(cap->reserved, 0, sizeof(cap->reserved)); + + return 0; +} + +static struct jpu_fmt *jpu_find_format(bool encoder, u32 pixelformat, + unsigned int fmt_type) +{ + unsigned int i, fmt_flag; + + if (encoder) + fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_ENC_OUTPUT : + JPU_ENC_CAPTURE; + else + fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_DEC_OUTPUT : + JPU_DEC_CAPTURE; + + for (i = 0; i < ARRAY_SIZE(jpu_formats); i++) { + struct jpu_fmt *fmt = &jpu_formats[i]; + + if (fmt->fourcc == pixelformat && fmt->types & fmt_flag) + return fmt; + } + + return NULL; +} + +static int jpu_enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + unsigned int i, num = 0; + + for (i = 0; i < ARRAY_SIZE(jpu_formats); ++i) { + if (jpu_formats[i].types & type) { + if (num == f->index) + break; + ++num; + } + } + + if (i >= ARRAY_SIZE(jpu_formats)) + return -EINVAL; + + f->pixelformat = jpu_formats[i].fourcc; + + return 0; +} + +static int jpu_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_CAPTURE : + JPU_DEC_CAPTURE); +} + +static int jpu_enum_fmt_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_OUTPUT : JPU_DEC_OUTPUT); +} + +static struct jpu_q_data *jpu_get_q_data(struct jpu_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->out_q; + else + return &ctx->cap_q; +} + +static void jpu_bound_align_image(u32 *w, unsigned int w_min, + unsigned int w_max, unsigned int w_align, + u32 *h, unsigned int h_min, + unsigned int h_max, unsigned int h_align) +{ + unsigned int width, height, w_step, h_step; + + width = *w; + height = *h; + + w_step = 1U << w_align; + h_step = 1U << h_align; + v4l_bound_align_image(w, w_min, w_max, w_align, h, h_min, h_max, + h_align, 3); + + if (*w < width && *w + w_step < w_max) + *w += w_step; + if (*h < height && *h + h_step < h_max) + *h += h_step; +} + +static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, + struct v4l2_pix_format_mplane *pix, + enum v4l2_buf_type type) +{ + struct jpu_fmt *fmt; + unsigned int f_type, w, h; + + f_type = V4L2_TYPE_IS_OUTPUT(type) ? JPU_FMT_TYPE_OUTPUT : + JPU_FMT_TYPE_CAPTURE; + + fmt = jpu_find_format(ctx->encoder, pix->pixelformat, f_type); + if (!fmt) { + unsigned int pixelformat; + + dev_dbg(ctx->jpu->dev, "unknown format; set default format\n"); + if (ctx->encoder) + pixelformat = f_type == JPU_FMT_TYPE_OUTPUT ? + V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; + else + pixelformat = f_type == JPU_FMT_TYPE_CAPTURE ? + V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; + fmt = jpu_find_format(ctx->encoder, pixelformat, f_type); + } + + pix->pixelformat = fmt->fourcc; + pix->colorspace = fmt->colorspace; + pix->field = V4L2_FIELD_NONE; + pix->num_planes = fmt->num_planes; + memset(pix->reserved, 0, sizeof(pix->reserved)); + + jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX, + fmt->h_align, &pix->height, JPU_HEIGHT_MIN, + JPU_HEIGHT_MAX, fmt->v_align); + + w = pix->width; + h = pix->height; + + if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { + /* ignore userspaces's sizeimage for encoding */ + if (pix->plane_fmt[0].sizeimage <= 0 || ctx->encoder) + pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE + + (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h); + pix->plane_fmt[0].bytesperline = 0; + memset(pix->plane_fmt[0].reserved, 0, + sizeof(pix->plane_fmt[0].reserved)); + } else { + unsigned int i, bpl = 0; + + for (i = 0; i < pix->num_planes; ++i) + bpl = max(bpl, pix->plane_fmt[i].bytesperline); + + bpl = clamp_t(unsigned int, bpl, w, JPU_WIDTH_MAX); + bpl = round_up(bpl, JPU_MEMALIGN); + + for (i = 0; i < pix->num_planes; ++i) { + pix->plane_fmt[i].bytesperline = bpl; + pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8; + memset(pix->plane_fmt[i].reserved, 0, + sizeof(pix->plane_fmt[i].reserved)); + } + } + + if (fmtinfo) + *fmtinfo = fmt; + + return 0; +} + +static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) + return -EINVAL; + + return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type); +} + +static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct jpu_ctx *ctx = fh_to_ctx(priv); + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct jpu_fmt *fmtinfo; + struct jpu_q_data *q_data; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = __jpu_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type); + if (ret < 0) + return ret; + + q_data = jpu_get_q_data(ctx, f->type); + + q_data->format = f->fmt.pix_mp; + q_data->fmtinfo = fmtinfo; + + return 0; +} + +static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct jpu_q_data *q_data; + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) + return -EINVAL; + + q_data = jpu_get_q_data(ctx, f->type); + f->fmt.pix_mp = q_data->format; + + return 0; +} + +/* + * V4L2 controls + */ +static int jpu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct jpu_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + + spin_lock_irqsave(&ctx->jpu->lock, flags); + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) + ctx->compr_quality = ctrl->val; + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + + return 0; +} + +static const struct v4l2_ctrl_ops jpu_ctrl_ops = { + .s_ctrl = jpu_s_ctrl, +}; + +static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + struct jpu_q_data *src_q_data, *dst_q_data, *orig, adj, *ref; + enum v4l2_buf_type adj_type; + + src_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + dst_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + if (ctx->encoder) { + adj = *src_q_data; + orig = src_q_data; + ref = dst_q_data; + adj_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } else { + adj = *dst_q_data; + orig = dst_q_data; + ref = src_q_data; + adj_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + } + + adj.format.width = ref->format.width; + adj.format.height = ref->format.height; + + __jpu_try_fmt(ctx, NULL, &adj.format, adj_type); + + if (adj.format.width != orig->format.width || + adj.format.height != orig->format.height) { + dev_err(ctx->jpu->dev, "src and dst formats do not match.\n"); + /* maybe we can return -EPIPE here? */ + return -EINVAL; + } + + return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops jpu_ioctl_ops = { + .vidioc_querycap = jpu_querycap, + + .vidioc_enum_fmt_vid_cap_mplane = jpu_enum_fmt_cap, + .vidioc_enum_fmt_vid_out_mplane = jpu_enum_fmt_out, + .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt, + .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt, + .vidioc_try_fmt_vid_out_mplane = jpu_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = jpu_s_fmt, + .vidioc_s_fmt_vid_out_mplane = jpu_s_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = jpu_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe +}; + +static int jpu_controls_create(struct jpu_ctx *ctx) +{ + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, &jpu_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, JPU_MAX_QUALITY - 1, 1, 0); + + if (ctx->ctrl_handler.error) { + ret = ctx->ctrl_handler.error; + goto error_free; + } + + if (!ctx->encoder) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (ret < 0) + goto error_free; + + return 0; + +error_free: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ret; +} + +/* + * ============================================================================ + * Queue operations + * ============================================================================ + */ +static int jpu_queue_setup(struct vb2_queue *vq, + const void *parg, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + const struct v4l2_format *fmt = parg; + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct jpu_q_data *q_data; + unsigned int i; + + q_data = jpu_get_q_data(ctx, vq->type); + + *nplanes = q_data->format.num_planes; + + for (i = 0; i < *nplanes; i++) { + unsigned int q_size = q_data->format.plane_fmt[i].sizeimage; + unsigned int f_size = fmt ? + fmt->fmt.pix_mp.plane_fmt[i].sizeimage : 0; + + if (fmt && f_size < q_size) + return -EINVAL; + + sizes[i] = fmt ? f_size : q_size; + alloc_ctxs[i] = ctx->jpu->alloc_ctx; + } + + return 0; +} + +static int jpu_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct jpu_q_data *q_data; + unsigned int i; + + q_data = jpu_get_q_data(ctx, vb->vb2_queue->type); + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dev_err(ctx->jpu->dev, "%s field isn't supported\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < q_data->format.num_planes; i++) { + unsigned long size = q_data->format.plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < size) { + dev_err(ctx->jpu->dev, + "%s: data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, i), size); + return -EINVAL; + } + + /* decoder capture queue */ + if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void jpu_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (!ctx->encoder && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf); + struct jpu_q_data *q_data, adjust; + void *buffer = vb2_plane_vaddr(vb, 0); + unsigned long buf_size = vb2_get_plane_payload(vb, 0); + unsigned int width, height; + + u8 subsampling = jpu_parse_hdr(buffer, buf_size, &width, + &height); + + /* check if JPEG data basic parsing was successful */ + if (subsampling != JPU_JPEG_422 && subsampling != JPU_JPEG_420) + goto format_error; + + q_data = &ctx->out_q; + + adjust = *q_data; + adjust.format.width = width; + adjust.format.height = height; + + __jpu_try_fmt(ctx, &adjust.fmtinfo, &adjust.format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + if (adjust.format.width != q_data->format.width || + adjust.format.height != q_data->format.height) + goto format_error; + + /* + * keep subsampling in buffer to check it + * for compatibility in device_run + */ + jpu_buf->subsampling = subsampling; + } + + if (ctx->fh.m2m_ctx) + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + + return; + +format_error: + dev_err(ctx->jpu->dev, "incompatible or corrupted JPEG data\n"); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +static void jpu_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf); + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct jpu_q_data *q_data = &ctx->out_q; + enum v4l2_buf_type type = vb->vb2_queue->type; + u8 *buffer; + + if (vb->state == VB2_BUF_STATE_DONE) + vbuf->sequence = jpu_get_q_data(ctx, type)->sequence++; + + if (!ctx->encoder || vb->state != VB2_BUF_STATE_DONE || + V4L2_TYPE_IS_OUTPUT(type)) + return; + + buffer = vb2_plane_vaddr(vb, 0); + + memcpy(buffer, jpeg_hdrs[jpu_buf->compr_quality], JPU_JPEG_HDR_SIZE); + *(__be16 *)(buffer + JPU_JPEG_HEIGHT_OFFSET) = + cpu_to_be16(q_data->format.height); + *(__be16 *)(buffer + JPU_JPEG_WIDTH_OFFSET) = + cpu_to_be16(q_data->format.width); + *(buffer + JPU_JPEG_SUBS_OFFSET) = q_data->fmtinfo->subsampling; +} + +static int jpu_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct jpu_q_data *q_data = jpu_get_q_data(ctx, vq->type); + + q_data->sequence = 0; + return 0; +} + +static void jpu_stop_streaming(struct vb2_queue *vq) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (vb == NULL) + return; + spin_lock_irqsave(&ctx->jpu->lock, flags); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + } +} + +static struct vb2_ops jpu_qops = { + .queue_setup = jpu_queue_setup, + .buf_prepare = jpu_buf_prepare, + .buf_queue = jpu_buf_queue, + .buf_finish = jpu_buf_finish, + .start_streaming = jpu_start_streaming, + .stop_streaming = jpu_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct jpu_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct jpu_buffer); + src_vq->ops = &jpu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->jpu->mutex; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct jpu_buffer); + dst_vq->ops = &jpu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->jpu->mutex; + + return vb2_queue_init(dst_vq); +} + +/* + * ============================================================================ + * Device file operations + * ============================================================================ + */ +static int jpu_open(struct file *file) +{ + struct jpu *jpu = video_drvdata(file); + struct video_device *vfd = video_devdata(file); + struct jpu_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + v4l2_fh_init(&ctx->fh, vfd); + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ctx->jpu = jpu; + ctx->encoder = vfd == &jpu->vfd_encoder; + + __jpu_try_fmt(ctx, &ctx->out_q.fmtinfo, &ctx->out_q.format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + __jpu_try_fmt(ctx, &ctx->cap_q.fmtinfo, &ctx->cap_q.format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpu->m2m_dev, ctx, jpu_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto v4l_prepare_rollback; + } + + ret = jpu_controls_create(ctx); + if (ret < 0) + goto v4l_prepare_rollback; + + if (mutex_lock_interruptible(&jpu->mutex)) { + ret = -ERESTARTSYS; + goto v4l_prepare_rollback; + } + + if (jpu->ref_count == 0) { + ret = clk_prepare_enable(jpu->clk); + if (ret < 0) + goto device_prepare_rollback; + /* ...issue software reset */ + ret = jpu_reset(jpu); + if (ret) + goto device_prepare_rollback; + } + + jpu->ref_count++; + + mutex_unlock(&jpu->mutex); + return 0; + +device_prepare_rollback: + mutex_unlock(&jpu->mutex); +v4l_prepare_rollback: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; +} + +static int jpu_release(struct file *file) +{ + struct jpu *jpu = video_drvdata(file); + struct jpu_ctx *ctx = fh_to_ctx(file->private_data); + + mutex_lock(&jpu->mutex); + if (--jpu->ref_count == 0) + clk_disable_unprepare(jpu->clk); + mutex_unlock(&jpu->mutex); + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations jpu_fops = { + .owner = THIS_MODULE, + .open = jpu_open, + .release = jpu_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +/* + * ============================================================================ + * mem2mem callbacks + * ============================================================================ + */ +static void jpu_cleanup(struct jpu_ctx *ctx, bool reset) +{ + /* remove current buffers and finish job */ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned long flags; + + spin_lock_irqsave(&ctx->jpu->lock, flags); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + + /* ...and give it a chance on next run */ + if (reset) + jpu_write(ctx->jpu, JCCMD_SRST, JCCMD); + + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + + v4l2_m2m_job_finish(ctx->jpu->m2m_dev, ctx->fh.m2m_ctx); +} + +static void jpu_device_run(void *priv) +{ + struct jpu_ctx *ctx = priv; + struct jpu *jpu = ctx->jpu; + struct jpu_buffer *jpu_buf; + struct jpu_q_data *q_data; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned int w, h, bpl; + unsigned char num_planes, subsampling; + unsigned long flags; + + /* ...wait until module reset completes; we have mutex locked here */ + if (jpu_wait_reset(jpu)) { + jpu_cleanup(ctx, true); + return; + } + + spin_lock_irqsave(&ctx->jpu->lock, flags); + + jpu->curr = ctx; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + if (ctx->encoder) { + jpu_buf = vb2_to_jpu_buffer(dst_buf); + q_data = &ctx->out_q; + } else { + jpu_buf = vb2_to_jpu_buffer(src_buf); + q_data = &ctx->cap_q; + } + + w = q_data->format.width; + h = q_data->format.height; + bpl = q_data->format.plane_fmt[0].bytesperline; + num_planes = q_data->fmtinfo->num_planes; + subsampling = q_data->fmtinfo->subsampling; + + if (ctx->encoder) { + unsigned long src_1_addr, src_2_addr, dst_addr; + unsigned int redu, inft; + + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + src_1_addr = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + if (num_planes > 1) + src_2_addr = vb2_dma_contig_plane_dma_addr( + &src_buf->vb2_buf, 1); + else + src_2_addr = src_1_addr + w * h; + + jpu_buf->compr_quality = ctx->compr_quality; + + if (subsampling == JPU_JPEG_420) { + redu = JCMOD_REDU_420; + inft = JIFECNT_INFT_420; + } else { + redu = JCMOD_REDU_422; + inft = JIFECNT_INFT_422; + } + + /* only no marker mode works for encoding */ + jpu_write(jpu, JCMOD_DSP_ENC | JCMOD_PCTR | redu | + JCMOD_MSKIP_ENABLE, JCMOD); + + jpu_write(jpu, JIFECNT_SWAP_WB | inft, JIFECNT); + jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); + jpu_write(jpu, JINTE_TRANSF_COMPL, JINTE); + + /* Y and C components source addresses */ + jpu_write(jpu, src_1_addr, JIFESYA1); + jpu_write(jpu, src_2_addr, JIFESCA1); + + /* memory width */ + jpu_write(jpu, bpl, JIFESMW); + + jpu_write(jpu, (w >> 8) & JCSZ_MASK, JCHSZU); + jpu_write(jpu, w & JCSZ_MASK, JCHSZD); + + jpu_write(jpu, (h >> 8) & JCSZ_MASK, JCVSZU); + jpu_write(jpu, h & JCSZ_MASK, JCVSZD); + + jpu_write(jpu, w, JIFESHSZ); + jpu_write(jpu, h, JIFESVSZ); + + jpu_write(jpu, dst_addr + JPU_JPEG_HDR_SIZE, JIFEDA1); + + jpu_write(jpu, 0 << JCQTN_SHIFT(1) | 1 << JCQTN_SHIFT(2) | + 1 << JCQTN_SHIFT(3), JCQTN); + + jpu_write(jpu, 0 << JCHTN_AC_SHIFT(1) | 0 << JCHTN_DC_SHIFT(1) | + 1 << JCHTN_AC_SHIFT(2) | 1 << JCHTN_DC_SHIFT(2) | + 1 << JCHTN_AC_SHIFT(3) | 1 << JCHTN_DC_SHIFT(3), + JCHTN); + + jpu_set_qtbl(jpu, ctx->compr_quality); + jpu_set_htbl(jpu); + } else { + unsigned long src_addr, dst_1_addr, dst_2_addr; + + if (jpu_buf->subsampling != subsampling) { + dev_err(ctx->jpu->dev, + "src and dst formats do not match.\n"); + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + jpu_cleanup(ctx, false); + return; + } + + src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + dst_1_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + if (q_data->fmtinfo->num_planes > 1) + dst_2_addr = vb2_dma_contig_plane_dma_addr( + &dst_buf->vb2_buf, 1); + else + dst_2_addr = dst_1_addr + w * h; + + /* ...set up decoder operation */ + jpu_write(jpu, JCMOD_DSP_DEC | JCMOD_PCTR, JCMOD); + jpu_write(jpu, JIFECNT_SWAP_WB, JIFECNT); + jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); + + /* ...enable interrupts on transfer completion and d-g error */ + jpu_write(jpu, JINTE_TRANSF_COMPL | JINTE_ERR, JINTE); + + /* ...set source/destination addresses of encoded data */ + jpu_write(jpu, src_addr, JIFDSA1); + jpu_write(jpu, dst_1_addr, JIFDDYA1); + jpu_write(jpu, dst_2_addr, JIFDDCA1); + + jpu_write(jpu, bpl, JIFDDMW); + } + + /* ...start encoder/decoder operation */ + jpu_write(jpu, JCCMD_JSRT, JCCMD); + + spin_unlock_irqrestore(&ctx->jpu->lock, flags); +} + +static int jpu_job_ready(void *priv) +{ + return 1; +} + +static void jpu_job_abort(void *priv) +{ + struct jpu_ctx *ctx = priv; + + if (!wait_event_timeout(ctx->jpu->irq_queue, !ctx->jpu->curr, + msecs_to_jiffies(JPU_JOB_TIMEOUT))) + jpu_cleanup(ctx, true); +} + +static struct v4l2_m2m_ops jpu_m2m_ops = { + .device_run = jpu_device_run, + .job_ready = jpu_job_ready, + .job_abort = jpu_job_abort, +}; + +/* + * ============================================================================ + * IRQ handler + * ============================================================================ + */ +static irqreturn_t jpu_irq_handler(int irq, void *dev_id) +{ + struct jpu *jpu = dev_id; + struct jpu_ctx *curr_ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + unsigned int int_status; + + int_status = jpu_read(jpu, JINTS); + + /* ...spurious interrupt */ + if (!((JINTS_TRANSF_COMPL | JINTS_PROCESS_COMPL | JINTS_ERR) & + int_status)) + return IRQ_NONE; + + /* ...clear interrupts */ + jpu_write(jpu, ~(int_status & JINTS_MASK), JINTS); + if (int_status & (JINTS_ERR | JINTS_PROCESS_COMPL)) + jpu_write(jpu, JCCMD_JEND, JCCMD); + + spin_lock(&jpu->lock); + + if ((int_status & JINTS_PROCESS_COMPL) && + !(int_status & JINTS_TRANSF_COMPL)) + goto handled; + + curr_ctx = v4l2_m2m_get_curr_priv(jpu->m2m_dev); + if (!curr_ctx) { + /* ...instance is not running */ + dev_err(jpu->dev, "no active context for m2m\n"); + goto handled; + } + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + if (int_status & JINTS_TRANSF_COMPL) { + if (curr_ctx->encoder) { + unsigned long payload_size = jpu_read(jpu, JCDTCU) << 16 + | jpu_read(jpu, JCDTCM) << 8 + | jpu_read(jpu, JCDTCD); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + payload_size + JPU_JPEG_HDR_SIZE); + } + + dst_buf->field = src_buf->field; + dst_buf->timestamp = src_buf->timestamp; + if (src_buf->flags & V4L2_BUF_FLAG_TIMECODE) + dst_buf->timecode = src_buf->timecode; + dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->flags |= src_buf->flags & + V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->flags = src_buf->flags & + (V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + } else if (int_status & JINTS_ERR) { + unsigned char error = jpu_read(jpu, JCDERR) & JCDERR_MASK; + + dev_dbg(jpu->dev, "processing error: %#X: %s\n", error, + error_to_text[error]); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } + + jpu->curr = NULL; + + /* ...reset JPU after completion */ + jpu_write(jpu, JCCMD_SRST, JCCMD); + spin_unlock(&jpu->lock); + + v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx); + + /* ...wakeup abort routine if needed */ + wake_up(&jpu->irq_queue); + + return IRQ_HANDLED; + +handled: + spin_unlock(&jpu->lock); + return IRQ_HANDLED; +} + +/* + * ============================================================================ + * Driver basic infrastructure + * ============================================================================ + */ +static const struct of_device_id jpu_dt_ids[] = { + { .compatible = "renesas,jpu-r8a7790" }, /* H2 */ + { .compatible = "renesas,jpu-r8a7791" }, /* M2-W */ + { .compatible = "renesas,jpu-r8a7792" }, /* V2H */ + { .compatible = "renesas,jpu-r8a7793" }, /* M2-N */ + { }, +}; +MODULE_DEVICE_TABLE(of, jpu_dt_ids); + +static int jpu_probe(struct platform_device *pdev) +{ + struct jpu *jpu; + struct resource *res; + int ret; + unsigned int i; + + jpu = devm_kzalloc(&pdev->dev, sizeof(*jpu), GFP_KERNEL); + if (!jpu) + return -ENOMEM; + + init_waitqueue_head(&jpu->irq_queue); + mutex_init(&jpu->mutex); + spin_lock_init(&jpu->lock); + jpu->dev = &pdev->dev; + + /* memory-mapped registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + jpu->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpu->regs)) + return PTR_ERR(jpu->regs); + + /* interrupt service routine registration */ + jpu->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0, + dev_name(&pdev->dev), jpu); + if (ret) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpu->irq); + return ret; + } + + /* clocks */ + jpu->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(jpu->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return PTR_ERR(jpu->clk); + } + + /* v4l2 device */ + ret = v4l2_device_register(&pdev->dev, &jpu->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* mem2mem device */ + jpu->m2m_dev = v4l2_m2m_init(&jpu_m2m_ops); + if (IS_ERR(jpu->m2m_dev)) { + v4l2_err(&jpu->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(jpu->m2m_dev); + goto device_register_rollback; + } + + jpu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(jpu->alloc_ctx)) { + v4l2_err(&jpu->v4l2_dev, "Failed to init memory allocator\n"); + ret = PTR_ERR(jpu->alloc_ctx); + goto m2m_init_rollback; + } + + /* fill in qantization and Huffman tables for encoder */ + for (i = 0; i < JPU_MAX_QUALITY; i++) + jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]); + + strlcpy(jpu->vfd_encoder.name, DRV_NAME, sizeof(jpu->vfd_encoder.name)); + jpu->vfd_encoder.fops = &jpu_fops; + jpu->vfd_encoder.ioctl_ops = &jpu_ioctl_ops; + jpu->vfd_encoder.minor = -1; + jpu->vfd_encoder.release = video_device_release_empty; + jpu->vfd_encoder.lock = &jpu->mutex; + jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev; + jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M; + + ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); + goto vb2_allocator_rollback; + } + + video_set_drvdata(&jpu->vfd_encoder, jpu); + + strlcpy(jpu->vfd_decoder.name, DRV_NAME, sizeof(jpu->vfd_decoder.name)); + jpu->vfd_decoder.fops = &jpu_fops; + jpu->vfd_decoder.ioctl_ops = &jpu_ioctl_ops; + jpu->vfd_decoder.minor = -1; + jpu->vfd_decoder.release = video_device_release_empty; + jpu->vfd_decoder.lock = &jpu->mutex; + jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev; + jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M; + + ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); + goto enc_vdev_register_rollback; + } + + video_set_drvdata(&jpu->vfd_decoder, jpu); + platform_set_drvdata(pdev, jpu); + + v4l2_info(&jpu->v4l2_dev, "encoder device registered as /dev/video%d\n", + jpu->vfd_encoder.num); + v4l2_info(&jpu->v4l2_dev, "decoder device registered as /dev/video%d\n", + jpu->vfd_decoder.num); + + return 0; + +enc_vdev_register_rollback: + video_unregister_device(&jpu->vfd_encoder); + +vb2_allocator_rollback: + vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); + +m2m_init_rollback: + v4l2_m2m_release(jpu->m2m_dev); + +device_register_rollback: + v4l2_device_unregister(&jpu->v4l2_dev); + + return ret; +} + +static int jpu_remove(struct platform_device *pdev) +{ + struct jpu *jpu = platform_get_drvdata(pdev); + + video_unregister_device(&jpu->vfd_decoder); + video_unregister_device(&jpu->vfd_encoder); + vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); + v4l2_m2m_release(jpu->m2m_dev); + v4l2_device_unregister(&jpu->v4l2_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int jpu_suspend(struct device *dev) +{ + struct jpu *jpu = dev_get_drvdata(dev); + + if (jpu->ref_count == 0) + return 0; + + clk_disable_unprepare(jpu->clk); + + return 0; +} + +static int jpu_resume(struct device *dev) +{ + struct jpu *jpu = dev_get_drvdata(dev); + + if (jpu->ref_count == 0) + return 0; + + clk_prepare_enable(jpu->clk); + + return 0; +} +#endif + +static const struct dev_pm_ops jpu_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume) +}; + +static struct platform_driver jpu_driver = { + .probe = jpu_probe, + .remove = jpu_remove, + .driver = { + .of_match_table = jpu_dt_ids, + .name = DRV_NAME, + .pm = &jpu_pm_ops, + }, +}; + +module_platform_driver(jpu_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Mikhail Ulianov "); +MODULE_DESCRIPTION("Renesas R-Car JPEG processing unit driver"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/media/platform/s3c-camif/camif-capture.c b/kernel/drivers/media/platform/s3c-camif/camif-capture.c index f6a61b9ce..537b858cb 100644 --- a/kernel/drivers/media/platform/s3c-camif/camif-capture.c +++ b/kernel/drivers/media/platform/s3c-camif/camif-capture.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include "camif-core.h" @@ -115,7 +115,7 @@ static int sensor_set_power(struct camif_dev *camif, int on) struct cam_sensor *sensor = &camif->sensor; int err = 0; - if (!on == camif->sensor.power_count) + if (camif->sensor.power_count == !on) err = v4l2_subdev_call(sensor->sd, core, s_power, on); if (!err) sensor->power_count += on ? 1 : -1; @@ -131,7 +131,7 @@ static int sensor_set_streaming(struct camif_dev *camif, int on) struct cam_sensor *sensor = &camif->sensor; int err = 0; - if (!on == camif->sensor.stream_count) + if (camif->sensor.stream_count == !on) err = v4l2_subdev_call(sensor->sd, video, s_stream, on); if (!err) sensor->stream_count += on ? 1 : -1; @@ -164,12 +164,12 @@ static int camif_reinitialize(struct camif_vp *vp) /* Release unused buffers */ while (!list_empty(&vp->pending_buf_q)) { buf = camif_pending_queue_pop(vp); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } while (!list_empty(&vp->active_buf_q)) { buf = camif_active_queue_pop(vp); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&camif->slock, flags); @@ -328,25 +328,19 @@ irqreturn_t s3c_camif_irq_handler(int irq, void *priv) !list_empty(&vp->active_buf_q)) { unsigned int index; struct camif_buffer *vbuf; - struct timeval *tv; - struct timespec ts; /* * Get previous DMA write buffer index: * 0 => DMA buffer 0, 2; * 1 => DMA buffer 1, 3. */ index = (CISTATUS_FRAMECNT(status) + 2) & 1; - - ktime_get_ts(&ts); vbuf = camif_active_queue_peek(vp, index); if (!WARN_ON(vbuf == NULL)) { /* Dequeue a filled buffer */ - tv = &vbuf->vb.v4l2_buf.timestamp; - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; - vbuf->vb.v4l2_buf.sequence = vp->frame_sequence++; - vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vbuf->vb.timestamp); + vbuf->vb.sequence = vp->frame_sequence++; + vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); /* Set up an empty buffer at the DMA engine */ vbuf = camif_pending_queue_pop(vp); @@ -441,27 +435,31 @@ static void stop_streaming(struct vb2_queue *vq) camif_stop_capture(vp); } -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *allocators[]) { + const struct v4l2_format *pfmt = parg; const struct v4l2_pix_format *pix = NULL; struct camif_vp *vp = vb2_get_drv_priv(vq); struct camif_dev *camif = vp->camif; struct camif_frame *frame = &vp->out_frame; - const struct camif_fmt *fmt = vp->out_fmt; + const struct camif_fmt *fmt; unsigned int size; if (pfmt) { pix = &pfmt->fmt.pix; fmt = s3c_camif_find_format(vp, &pix->pixelformat, -1); + if (fmt == NULL) + return -EINVAL; size = (pix->width * pix->height * fmt->depth) / 8; } else { + fmt = vp->out_fmt; + if (fmt == NULL) + return -EINVAL; size = (frame->f_width * frame->f_height * fmt->depth) / 8; } - if (fmt == NULL) - return -EINVAL; *num_planes = 1; if (pix) @@ -493,13 +491,14 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { - struct camif_buffer *buf = container_of(vb, struct camif_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct camif_buffer *buf = container_of(vbuf, struct camif_buffer, vb); struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); struct camif_dev *camif = vp->camif; unsigned long flags; spin_lock_irqsave(&camif->slock, flags); - WARN_ON(camif_prepare_addr(vp, &buf->vb, &buf->paddr)); + WARN_ON(camif_prepare_addr(vp, &buf->vb.vb2_buf, &buf->paddr)); if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) { /* Schedule an empty buffer in H/W */ diff --git a/kernel/drivers/media/platform/s3c-camif/camif-core.c b/kernel/drivers/media/platform/s3c-camif/camif-core.c index 2d5bd3ac7..1ba9bb08f 100644 --- a/kernel/drivers/media/platform/s3c-camif/camif-core.c +++ b/kernel/drivers/media/platform/s3c-camif/camif-core.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "camif-core.h" @@ -628,7 +628,7 @@ static struct s3c_camif_drvdata s3c6410_camif_drvdata = { .bus_clk_freq = 133000000UL, }; -static struct platform_device_id s3c_camif_driver_ids[] = { +static const struct platform_device_id s3c_camif_driver_ids[] = { { .name = "s3c2440-camif", .driver_data = (unsigned long)&s3c244x_camif_drvdata, diff --git a/kernel/drivers/media/platform/s3c-camif/camif-core.h b/kernel/drivers/media/platform/s3c-camif/camif-core.h index 35d2fcdc0..adaf1969e 100644 --- a/kernel/drivers/media/platform/s3c-camif/camif-core.h +++ b/kernel/drivers/media/platform/s3c-camif/camif-core.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #define S3C_CAMIF_DRIVER_NAME "s3c-camif" @@ -322,7 +322,7 @@ struct camif_addr { * @index: an identifier of this buffer at the DMA engine */ struct camif_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; struct camif_addr paddr; unsigned int index; diff --git a/kernel/drivers/media/platform/s5p-g2d/g2d.c b/kernel/drivers/media/platform/s5p-g2d/g2d.c index ec3e12489..e1936d9d2 100644 --- a/kernel/drivers/media/platform/s5p-g2d/g2d.c +++ b/kernel/drivers/media/platform/s5p-g2d/g2d.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include "g2d.h" @@ -101,7 +101,7 @@ static struct g2d_frame *get_frame(struct g2d_ctx *ctx, } } -static int g2d_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int g2d_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -134,8 +134,9 @@ static int g2d_buf_prepare(struct vb2_buffer *vb) static void g2d_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static struct vb2_ops g2d_qops = { @@ -537,7 +538,7 @@ static irqreturn_t g2d_isr(int irq, void *prv) { struct g2d_dev *dev = prv; struct g2d_ctx *ctx = dev->curr; - struct vb2_buffer *src, *dst; + struct vb2_v4l2_buffer *src, *dst; g2d_clear_int(dev); clk_disable(dev->gate); @@ -550,11 +551,11 @@ static irqreturn_t g2d_isr(int irq, void *prv) BUG_ON(src == NULL); BUG_ON(dst == NULL); - dst->v4l2_buf.timecode = src->v4l2_buf.timecode; - dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; - dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst->v4l2_buf.flags |= - src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->timecode = src->timecode; + dst->timestamp = src->timestamp; + dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->flags |= + src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); @@ -787,7 +788,7 @@ static const struct of_device_id exynos_g2d_match[] = { }; MODULE_DEVICE_TABLE(of, exynos_g2d_match); -static struct platform_device_id g2d_driver_ids[] = { +static const struct platform_device_id g2d_driver_ids[] = { { .name = "s5p-g2d", .driver_data = (unsigned long)&g2d_drvdata_v3x, diff --git a/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.c b/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.c index bfbf15756..4a608cbe0 100644 --- a/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include "jpeg-core.h" @@ -626,6 +626,7 @@ static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) return V4L2_JPEG_CHROMA_SUBSAMPLING_411; return exynos3250_decoded_subsampling[ctx->subsampling]; case SJPEG_EXYNOS4: + case SJPEG_EXYNOS5433: if (ctx->subsampling > 2) return V4L2_JPEG_CHROMA_SUBSAMPLING_420; return exynos4x12_decoded_subsampling[ctx->subsampling]; @@ -750,6 +751,208 @@ static void exynos4_jpeg_set_huff_tbl(void __iomem *base) ARRAY_SIZE(hactblg0)); } +static inline int __exynos4_huff_tbl(int class, int id, bool lenval) +{ + /* + * class: 0 - DC, 1 - AC + * id: 0 - Y, 1 - Cb/Cr + */ + if (class) { + if (id) + return lenval ? EXYNOS4_HUFF_TBL_HACCL : + EXYNOS4_HUFF_TBL_HACCV; + return lenval ? EXYNOS4_HUFF_TBL_HACLL : EXYNOS4_HUFF_TBL_HACLV; + + } + /* class == 0 */ + if (id) + return lenval ? EXYNOS4_HUFF_TBL_HDCCL : EXYNOS4_HUFF_TBL_HDCCV; + + return lenval ? EXYNOS4_HUFF_TBL_HDCLL : EXYNOS4_HUFF_TBL_HDCLV; +} + +static inline int exynos4_huff_tbl_len(int class, int id) +{ + return __exynos4_huff_tbl(class, id, true); +} + +static inline int exynos4_huff_tbl_val(int class, int id) +{ + return __exynos4_huff_tbl(class, id, false); +} + +static int get_byte(struct s5p_jpeg_buffer *buf); +static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word); +static void skip(struct s5p_jpeg_buffer *buf, long len); + +static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, x, components; + + jpeg_buffer.size = 2; /* Ls */ + jpeg_buffer.data = + (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sos + 2; + jpeg_buffer.curr = 0; + + word = 0; + + if (get_word_be(&jpeg_buffer, &word)) + return; + jpeg_buffer.size = (long)word - 2; + jpeg_buffer.data += 2; + jpeg_buffer.curr = 0; + + components = get_byte(&jpeg_buffer); + if (components == -1) + return; + while (components--) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + x = get_byte(&jpeg_buffer); + if (x == -1) + return; + exynos4_jpeg_select_dec_h_tbl(jpeg->regs, c, + (((x >> 4) & 0x1) << 1) | (x & 0x1)); + } + +} + +static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, i, n, j; + + for (j = 0; j < ctx->out_q.dht.n; ++j) { + jpeg_buffer.size = ctx->out_q.dht.len[j]; + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + ctx->out_q.dht.marker[j]; + jpeg_buffer.curr = 0; + + word = 0; + while (jpeg_buffer.curr < jpeg_buffer.size) { + char id, class; + + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + id = c & 0xf; + class = (c >> 4) & 0xf; + n = 0; + for (i = 0; i < 16; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + exynos4_huff_tbl_len(class, id) + + (i / 4) * 4); + word = 0; + } + n += c; + } + word = 0; + for (i = 0; i < n; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + exynos4_huff_tbl_val(class, id) + + (i / 4) * 4); + word = 0; + } + } + if (i % 4) { + writel(word, jpeg->regs + + exynos4_huff_tbl_val(class, id) + (i / 4) * 4); + } + word = 0; + } + } +} + +static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + int c, x, components; + + jpeg_buffer.size = ctx->out_q.sof_len; + jpeg_buffer.data = + (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sof; + jpeg_buffer.curr = 0; + + skip(&jpeg_buffer, 5); /* P, Y, X */ + components = get_byte(&jpeg_buffer); + if (components == -1) + return; + + exynos4_jpeg_set_dec_components(jpeg->regs, components); + + while (components--) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + skip(&jpeg_buffer, 1); + x = get_byte(&jpeg_buffer); + if (x == -1) + return; + exynos4_jpeg_select_dec_q_tbl(jpeg->regs, c, x); + } +} + +static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx) +{ + struct s5p_jpeg *jpeg = ctx->jpeg; + struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct s5p_jpeg_buffer jpeg_buffer; + unsigned int word; + int c, i, j; + + for (j = 0; j < ctx->out_q.dqt.n; ++j) { + jpeg_buffer.size = ctx->out_q.dqt.len[j]; + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + ctx->out_q.dqt.marker[j]; + jpeg_buffer.curr = 0; + + word = 0; + while (jpeg_buffer.size - jpeg_buffer.curr >= 65) { + char id; + + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + id = c & 0xf; + /* nonzero means extended mode - not supported */ + if ((c >> 4) & 0xf) + return; + for (i = 0; i < 64; ++i) { + c = get_byte(&jpeg_buffer); + if (c == -1) + return; + word |= c << ((i % 4) * 8); + if ((i + 1) % 4 == 0) { + writel(word, jpeg->regs + + EXYNOS4_QTBL_CONTENT(id) + (i / 4) * 4); + word = 0; + } + } + word = 0; + } + } +} + /* * ============================================================================ * Device file operations @@ -894,8 +1097,11 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, unsigned long buffer, unsigned long size, struct s5p_jpeg_ctx *ctx) { - int c, components = 0, notfound; - unsigned int height, width, word, subsampling = 0; + 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]; long length; struct s5p_jpeg_buffer jpeg_buffer; @@ -904,7 +1110,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, jpeg_buffer.curr = 0; notfound = 1; - while (notfound) { + while (notfound || !sos) { c = get_byte(&jpeg_buffer); if (c == -1) return false; @@ -923,6 +1129,11 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, case SOF0: if (get_word_be(&jpeg_buffer, &word)) break; + length = (long)word - 2; + if (!length) + return false; + sof = jpeg_buffer.curr; /* after 0xffc0 */ + sof_len = length; if (get_byte(&jpeg_buffer) == -1) break; if (get_word_be(&jpeg_buffer, &height)) @@ -932,7 +1143,6 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, components = get_byte(&jpeg_buffer); if (components == -1) break; - notfound = 0; if (components == 1) { subsampling = 0x33; @@ -941,8 +1151,40 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, subsampling = get_byte(&jpeg_buffer); skip(&jpeg_buffer, 1); } - + if (components > 3) + return false; skip(&jpeg_buffer, components * 2); + notfound = 0; + break; + + case DQT: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + if (!length) + return false; + if (n_dqt >= S5P_JPEG_MAX_MARKER) + return false; + dqt[n_dqt] = jpeg_buffer.curr; /* after 0xffdb */ + dqt_len[n_dqt++] = length; + skip(&jpeg_buffer, length); + break; + + case DHT: + if (get_word_be(&jpeg_buffer, &word)) + break; + length = (long)word - 2; + if (!length) + return false; + if (n_dht >= S5P_JPEG_MAX_MARKER) + return false; + dht[n_dht] = jpeg_buffer.curr; /* after 0xffc4 */ + dht_len[n_dht++] = length; + skip(&jpeg_buffer, length); + break; + + case SOS: + sos = jpeg_buffer.curr - 2; /* 0xffda */ break; /* skip payload-less markers */ @@ -963,7 +1205,20 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, } result->w = width; result->h = height; - result->size = components; + result->sos = sos; + result->dht.n = n_dht; + while (n_dht--) { + result->dht.marker[n_dht] = dht[n_dht]; + result->dht.len[n_dht] = dht_len[n_dht]; + } + result->dqt.n = n_dqt; + while (n_dqt--) { + result->dqt.marker[n_dqt] = dqt[n_dqt]; + result->dqt.len[n_dqt] = dqt_len[n_dqt]; + } + result->sof = sof; + result->sof_len = sof_len; + result->size = result->components = components; switch (subsampling) { case 0x11: @@ -982,7 +1237,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, return false; } - return !notfound; + return !notfound && sos; } static int s5p_jpeg_querycap(struct file *file, void *priv, @@ -1226,8 +1481,7 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) || - (ctx->mode != S5P_JPEG_DECODE)) + if (!ctx->jpeg->variant->hw_ex4_compat || ctx->mode != S5P_JPEG_DECODE) goto exit; /* @@ -1350,7 +1604,7 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu * page fault calculate proper buffer size in such a case. */ - if (ct->jpeg->variant->version == SJPEG_EXYNOS4 && + if (ct->jpeg->variant->hw_ex4_compat && f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE) q_data->size = exynos4_jpeg_get_output_buffer_size(ct, f, @@ -1889,9 +2143,36 @@ static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (jpeg->variant->version == SJPEG_EXYNOS5433 && + ctx->mode == S5P_JPEG_DECODE) + jpeg_addr += ctx->out_q.sos; exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr); } +static inline void exynos4_jpeg_set_img_fmt(void __iomem *base, + unsigned int img_fmt) +{ + __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS4); +} + +static inline void exynos5433_jpeg_set_img_fmt(void __iomem *base, + unsigned int img_fmt) +{ + __exynos4_jpeg_set_img_fmt(base, img_fmt, SJPEG_EXYNOS5433); +} + +static inline void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, + unsigned int out_fmt) +{ + __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS4); +} + +static inline void exynos5433_jpeg_set_enc_out_fmt(void __iomem *base, + unsigned int out_fmt) +{ + __exynos4_jpeg_set_enc_out_fmt(base, out_fmt, SJPEG_EXYNOS5433); +} + static void exynos4_jpeg_device_run(void *priv) { struct s5p_jpeg_ctx *ctx = priv; @@ -1899,11 +2180,11 @@ static void exynos4_jpeg_device_run(void *priv) unsigned int bitstream_size; unsigned long flags; - spin_lock_irqsave(&ctx->jpeg->slock, flags); + spin_lock_irqsave(&jpeg->slock, flags); if (ctx->mode == S5P_JPEG_ENCODE) { exynos4_jpeg_sw_reset(jpeg->regs); - exynos4_jpeg_set_interrupt(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs, jpeg->variant->version); exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); exynos4_jpeg_set_huff_tbl(jpeg->regs); @@ -1920,27 +2201,56 @@ static void exynos4_jpeg_device_run(void *priv) exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, ctx->cap_q.h); - exynos4_jpeg_set_enc_out_fmt(jpeg->regs, ctx->subsampling); - exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->out_q.fmt->fourcc); + if (ctx->jpeg->variant->version == SJPEG_EXYNOS4) { + exynos4_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos4_jpeg_set_img_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } else { + exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos5433_jpeg_set_img_fmt(jpeg->regs, + ctx->out_q.fmt->fourcc); + } exynos4_jpeg_set_img_addr(ctx); exynos4_jpeg_set_jpeg_addr(ctx); exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs, ctx->out_q.fmt->fourcc); } else { exynos4_jpeg_sw_reset(jpeg->regs); - exynos4_jpeg_set_interrupt(jpeg->regs); + exynos4_jpeg_set_interrupt(jpeg->regs, + jpeg->variant->version); exynos4_jpeg_set_img_addr(ctx); exynos4_jpeg_set_jpeg_addr(ctx); - exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->cap_q.fmt->fourcc); - bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); + if (jpeg->variant->version == SJPEG_EXYNOS5433) { + exynos4_jpeg_parse_huff_tbl(ctx); + exynos4_jpeg_parse_decode_h_tbl(ctx); + + exynos4_jpeg_parse_q_tbl(ctx); + exynos4_jpeg_parse_decode_q_tbl(ctx); + + exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); + + exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, + ctx->cap_q.h); + exynos5433_jpeg_set_enc_out_fmt(jpeg->regs, + ctx->subsampling); + exynos5433_jpeg_set_img_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 16); + } else { + exynos4_jpeg_set_img_fmt(jpeg->regs, + ctx->cap_q.fmt->fourcc); + bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); + } exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size); } exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode); - spin_unlock_irqrestore(&ctx->jpeg->slock, flags); + spin_unlock_irqrestore(&jpeg->slock, flags); } static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) @@ -2120,7 +2430,7 @@ static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { */ static int s5p_jpeg_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -2170,6 +2480,7 @@ static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb) static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); if (ctx->mode == S5P_JPEG_DECODE && @@ -2187,13 +2498,24 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) q_data = &ctx->out_q; q_data->w = tmp.w; q_data->h = tmp.h; + q_data->sos = tmp.sos; + memcpy(q_data->dht.marker, tmp.dht.marker, + sizeof(tmp.dht.marker)); + memcpy(q_data->dht.len, tmp.dht.len, sizeof(tmp.dht.len)); + q_data->dht.n = tmp.dht.n; + memcpy(q_data->dqt.marker, tmp.dqt.marker, + sizeof(tmp.dqt.marker)); + memcpy(q_data->dqt.len, tmp.dqt.len, sizeof(tmp.dqt.len)); + q_data->dqt.n = tmp.dqt.n; + q_data->sof = tmp.sof; + q_data->sof_len = tmp.sof_len; q_data = &ctx->cap_q; q_data->w = tmp.w; q_data->h = tmp.h; } - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) @@ -2264,7 +2586,7 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) { struct s5p_jpeg *jpeg = dev_id; struct s5p_jpeg_ctx *curr_ctx; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; unsigned long payload_size = 0; enum vb2_buffer_state state = VB2_BUF_STATE_DONE; bool enc_jpeg_too_large = false; @@ -2298,15 +2620,15 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) payload_size = s5p_jpeg_compressed_size(jpeg->regs); } - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; - dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->v4l2_buf.flags |= - src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->timecode = src_buf->timecode; + dst_buf->timestamp = src_buf->timestamp; + dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->flags |= + src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; v4l2_m2m_buf_done(src_buf, state); if (curr_ctx->mode == S5P_JPEG_ENCODE) - vb2_set_plane_payload(dst_buf, 0, payload_size); + 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); @@ -2321,7 +2643,7 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) { unsigned int int_status; - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; struct s5p_jpeg *jpeg = priv; struct s5p_jpeg_ctx *curr_ctx; unsigned long payload_size = 0; @@ -2363,7 +2685,8 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) if (jpeg->irq_ret == OK_ENC_OR_DEC) { if (curr_ctx->mode == S5P_JPEG_ENCODE) { payload_size = exynos4_jpeg_get_stream_size(jpeg->regs); - vb2_set_plane_payload(dst_vb, 0, payload_size); + vb2_set_plane_payload(&dst_vb->vb2_buf, + 0, payload_size); } v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -2373,7 +2696,8 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) } v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); - curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); + if (jpeg->variant->version == SJPEG_EXYNOS4) + curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); spin_unlock(&jpeg->slock); return IRQ_HANDLED; @@ -2383,7 +2707,7 @@ static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) { struct s5p_jpeg *jpeg = dev_id; struct s5p_jpeg_ctx *curr_ctx; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; unsigned long payload_size = 0; enum vb2_buffer_state state = VB2_BUF_STATE_DONE; bool interrupt_timeout = false; @@ -2427,12 +2751,12 @@ static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id) src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; + dst_buf->timestamp = src_buf->timestamp; v4l2_m2m_buf_done(src_buf, state); if (curr_ctx->mode == S5P_JPEG_ENCODE) - vb2_set_plane_payload(dst_buf, 0, payload_size); + 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); @@ -2455,7 +2779,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) { struct s5p_jpeg *jpeg; struct resource *res; - int ret; + int i, ret; /* JPEG IP abstraction struct */ jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); @@ -2490,23 +2814,21 @@ static int s5p_jpeg_probe(struct platform_device *pdev) } /* clocks */ - jpeg->clk = clk_get(&pdev->dev, "jpeg"); - if (IS_ERR(jpeg->clk)) { - dev_err(&pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(jpeg->clk); - return ret; + for (i = 0; i < jpeg->variant->num_clocks; i++) { + jpeg->clocks[i] = devm_clk_get(&pdev->dev, + jpeg->variant->clk_names[i]); + if (IS_ERR(jpeg->clocks[i])) { + dev_err(&pdev->dev, "failed to get clock: %s\n", + jpeg->variant->clk_names[i]); + return PTR_ERR(jpeg->clocks[i]); + } } - dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); - - jpeg->sclk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(jpeg->sclk)) - dev_info(&pdev->dev, "sclk clock not available\n"); /* v4l2 device */ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - goto clk_get_rollback; + return ret; } /* mem2mem device */ @@ -2544,7 +2866,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto enc_vdev_alloc_rollback; + video_device_release(jpeg->vfd_encoder); + goto vb2_allocator_rollback; } video_set_drvdata(jpeg->vfd_encoder, jpeg); @@ -2572,7 +2895,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto dec_vdev_alloc_rollback; + video_device_release(jpeg->vfd_decoder); + goto enc_vdev_register_rollback; } video_set_drvdata(jpeg->vfd_decoder, jpeg); @@ -2589,15 +2913,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return 0; -dec_vdev_alloc_rollback: - video_device_release(jpeg->vfd_decoder); - enc_vdev_register_rollback: video_unregister_device(jpeg->vfd_encoder); -enc_vdev_alloc_rollback: - video_device_release(jpeg->vfd_encoder); - vb2_allocator_rollback: vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); @@ -2607,38 +2925,27 @@ m2m_init_rollback: device_register_rollback: v4l2_device_unregister(&jpeg->v4l2_dev); -clk_get_rollback: - clk_put(jpeg->clk); - if (!IS_ERR(jpeg->sclk)) - clk_put(jpeg->sclk); - return ret; } static int s5p_jpeg_remove(struct platform_device *pdev) { struct s5p_jpeg *jpeg = platform_get_drvdata(pdev); + int i; pm_runtime_disable(jpeg->dev); video_unregister_device(jpeg->vfd_decoder); - video_device_release(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); - video_device_release(jpeg->vfd_encoder); vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); if (!pm_runtime_status_suspended(&pdev->dev)) { - clk_disable_unprepare(jpeg->clk); - if (!IS_ERR(jpeg->sclk)) - clk_disable_unprepare(jpeg->sclk); + for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(jpeg->clocks[i]); } - clk_put(jpeg->clk); - if (!IS_ERR(jpeg->sclk)) - clk_put(jpeg->sclk); - return 0; } @@ -2646,10 +2953,10 @@ static int s5p_jpeg_remove(struct platform_device *pdev) static int s5p_jpeg_runtime_suspend(struct device *dev) { struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + int i; - clk_disable_unprepare(jpeg->clk); - if (!IS_ERR(jpeg->sclk)) - clk_disable_unprepare(jpeg->sclk); + for (i = jpeg->variant->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(jpeg->clocks[i]); return 0; } @@ -2658,16 +2965,15 @@ static int s5p_jpeg_runtime_resume(struct device *dev) { struct s5p_jpeg *jpeg = dev_get_drvdata(dev); unsigned long flags; - int ret; + int i, ret; - ret = clk_prepare_enable(jpeg->clk); - if (ret < 0) - return ret; - - if (!IS_ERR(jpeg->sclk)) { - ret = clk_prepare_enable(jpeg->sclk); - if (ret < 0) + for (i = 0; i < jpeg->variant->num_clocks; i++) { + ret = clk_prepare_enable(jpeg->clocks[i]); + if (ret) { + while (--i > 0) + clk_disable_unprepare(jpeg->clocks[i]); return ret; + } } spin_lock_irqsave(&jpeg->slock, flags); @@ -2721,6 +3027,8 @@ static struct s5p_jpeg_variant s5p_jpeg_drvdata = { .jpeg_irq = s5p_jpeg_irq, .m2m_ops = &s5p_jpeg_m2m_ops, .fmt_ver_flag = SJPEG_FMT_FLAG_S5P, + .clk_names = {"jpeg"}, + .num_clocks = 1, }; static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { @@ -2729,6 +3037,8 @@ static struct s5p_jpeg_variant exynos3250_jpeg_drvdata = { .m2m_ops = &exynos3250_jpeg_m2m_ops, .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, .hw3250_compat = 1, + .clk_names = {"jpeg", "sclk"}, + .num_clocks = 2, }; static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { @@ -2737,6 +3047,9 @@ static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { .m2m_ops = &exynos4_jpeg_m2m_ops, .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, .htbl_reinit = 1, + .clk_names = {"jpeg"}, + .num_clocks = 1, + .hw_ex4_compat = 1, }; static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = { @@ -2746,6 +3059,19 @@ static struct s5p_jpeg_variant exynos5420_jpeg_drvdata = { .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS3250, /* intentionally 3250 */ .hw3250_compat = 1, .htbl_reinit = 1, + .clk_names = {"jpeg"}, + .num_clocks = 1, +}; + +static struct s5p_jpeg_variant exynos5433_jpeg_drvdata = { + .version = SJPEG_EXYNOS5433, + .jpeg_irq = exynos4_jpeg_irq, + .m2m_ops = &exynos4_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, + .htbl_reinit = 1, + .clk_names = {"pclk", "aclk", "aclk_xiu", "sclk"}, + .num_clocks = 4, + .hw_ex4_compat = 1, }; static const struct of_device_id samsung_jpeg_match[] = { @@ -2764,6 +3090,9 @@ static const struct of_device_id samsung_jpeg_match[] = { }, { .compatible = "samsung,exynos5420-jpeg", .data = &exynos5420_jpeg_drvdata, + }, { + .compatible = "samsung,exynos5433-jpeg", + .data = &exynos5433_jpeg_drvdata, }, {}, }; diff --git a/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.h b/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.h index 7d9a9ed19..9b1db0934 100644 --- a/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/kernel/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -20,6 +20,8 @@ #define S5P_JPEG_M2M_NAME "s5p-jpeg" +#define JPEG_MAX_CLOCKS 4 + /* JPEG compression quality setting */ #define S5P_JPEG_COMPR_QUAL_BEST 0 #define S5P_JPEG_COMPR_QUAL_WORST 3 @@ -40,9 +42,12 @@ /* a selection of JPEG markers */ #define TEM 0x01 #define SOF0 0xc0 +#define DHT 0xc4 #define RST 0xd0 #define SOI 0xd8 #define EOI 0xd9 +#define SOS 0xda +#define DQT 0xdb #define DHP 0xde /* Flags that indicate a format can be used for capture/output */ @@ -66,12 +71,15 @@ #define SJPEG_SUBSAMPLING_422 0x21 #define SJPEG_SUBSAMPLING_420 0x22 +#define S5P_JPEG_MAX_MARKER 4 + /* Version numbers */ enum sjpeg_version { SJPEG_S5P, SJPEG_EXYNOS3250, SJPEG_EXYNOS4, SJPEG_EXYNOS5420, + SJPEG_EXYNOS5433, }; enum exynos4_jpeg_result { @@ -100,8 +108,7 @@ enum exynos4_jpeg_img_quality_level { * @m2m_dev: v4l2 mem2mem device data * @regs: JPEG IP registers mapping * @irq: JPEG IP irq - * @clk: JPEG IP clock - * @sclk: Exynos3250 JPEG IP special clock + * @clocks: JPEG IP clock(s) * @dev: JPEG IP struct device * @alloc_ctx: videobuf2 memory allocator's context * @variant: driver variant to be used @@ -121,8 +128,7 @@ struct s5p_jpeg { void __iomem *regs; unsigned int irq; enum exynos4_jpeg_result irq_ret; - struct clk *clk; - struct clk *sclk; + struct clk *clocks[JPEG_MAX_CLOCKS]; struct device *dev; void *alloc_ctx; struct s5p_jpeg_variant *variant; @@ -134,8 +140,11 @@ struct s5p_jpeg_variant { unsigned int fmt_ver_flag; unsigned int hw3250_compat:1; unsigned int htbl_reinit:1; + unsigned int hw_ex4_compat:1; struct v4l2_m2m_ops *m2m_ops; irqreturn_t (*jpeg_irq)(int irq, void *priv); + const char *clk_names[JPEG_MAX_CLOCKS]; + int num_clocks; }; /** @@ -160,17 +169,41 @@ struct s5p_jpeg_fmt { u32 flags; }; +/** + * s5p_jpeg_marker - collection of markers from jpeg header + * @marker: markers' positions relative to the buffer beginning + * @len: markers' payload lengths (without length field) + * @n: number of markers in collection + */ +struct s5p_jpeg_marker { + u32 marker[S5P_JPEG_MAX_MARKER]; + u32 len[S5P_JPEG_MAX_MARKER]; + u32 n; +}; + /** * s5p_jpeg_q_data - parameters of one queue * @fmt: driver-specific format of this queue * @w: image width * @h: image height + * @sos: SOS marker's position relative to the buffer beginning + * @dht: DHT markers' positions relative to the buffer beginning + * @dqt: DQT markers' positions relative to the buffer beginning + * @sof: SOF0 marker's postition relative to the buffer beginning + * @sof_len: SOF0 marker's payload length (without length field itself) + * @components: number of image components * @size: image buffer size in bytes */ struct s5p_jpeg_q_data { struct s5p_jpeg_fmt *fmt; u32 w; u32 h; + u32 sos; + struct s5p_jpeg_marker dht; + struct s5p_jpeg_marker dqt; + u32 sof; + u32 sof_len; + u32 components; u32 size; }; diff --git a/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c index ab6d6f43c..0912d0a89 100644 --- a/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c +++ b/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c @@ -45,9 +45,20 @@ void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode) } } -void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt) +void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, + unsigned int version) { unsigned int reg; + unsigned int exynos4_swap_chroma_cbcr; + unsigned int exynos4_swap_chroma_crcb; + + if (version == SJPEG_EXYNOS4) { + exynos4_swap_chroma_cbcr = EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_crcb = EXYNOS4_SWAP_CHROMA_CRCB; + } else { + exynos4_swap_chroma_cbcr = EXYNOS5433_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_crcb = EXYNOS5433_SWAP_CHROMA_CRCB; + } reg = readl(base + EXYNOS4_IMG_FMT_REG) & EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */ @@ -67,48 +78,48 @@ void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt) case V4L2_PIX_FMT_NV24: reg = reg | EXYNOS4_ENC_YUV_444_IMG | EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | - EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_cbcr; break; case V4L2_PIX_FMT_NV42: reg = reg | EXYNOS4_ENC_YUV_444_IMG | EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | - EXYNOS4_SWAP_CHROMA_CRCB; + exynos4_swap_chroma_crcb; break; case V4L2_PIX_FMT_YUYV: reg = reg | EXYNOS4_DEC_YUV_422_IMG | EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | - EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_cbcr; break; case V4L2_PIX_FMT_YVYU: reg = reg | EXYNOS4_DEC_YUV_422_IMG | EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | - EXYNOS4_SWAP_CHROMA_CRCB; + exynos4_swap_chroma_crcb; break; case V4L2_PIX_FMT_NV16: reg = reg | EXYNOS4_DEC_YUV_422_IMG | EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | - EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_cbcr; break; case V4L2_PIX_FMT_NV61: reg = reg | EXYNOS4_DEC_YUV_422_IMG | EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | - EXYNOS4_SWAP_CHROMA_CRCB; + exynos4_swap_chroma_crcb; break; case V4L2_PIX_FMT_NV12: reg = reg | EXYNOS4_DEC_YUV_420_IMG | EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | - EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_cbcr; break; case V4L2_PIX_FMT_NV21: reg = reg | EXYNOS4_DEC_YUV_420_IMG | EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | - EXYNOS4_SWAP_CHROMA_CRCB; + exynos4_swap_chroma_crcb; break; case V4L2_PIX_FMT_YUV420: reg = reg | EXYNOS4_DEC_YUV_420_IMG | EXYNOS4_YUV_420_IP_YUV_420_3P_IMG | - EXYNOS4_SWAP_CHROMA_CBCR; + exynos4_swap_chroma_cbcr; break; default: break; @@ -118,12 +129,14 @@ void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt) writel(reg, base + EXYNOS4_IMG_FMT_REG); } -void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt) +void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, + unsigned int version) { unsigned int reg; reg = readl(base + EXYNOS4_IMG_FMT_REG) & - ~EXYNOS4_ENC_FMT_MASK; /* clear enc format */ + ~(version == SJPEG_EXYNOS4 ? EXYNOS4_ENC_FMT_MASK : + EXYNOS5433_ENC_FMT_MASK); /* clear enc format */ switch (out_fmt) { case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: @@ -149,9 +162,18 @@ void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt) writel(reg, base + EXYNOS4_IMG_FMT_REG); } -void exynos4_jpeg_set_interrupt(void __iomem *base) +void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version) { - writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); + unsigned int reg; + + if (version == SJPEG_EXYNOS4) { + reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; + writel(reg | EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); + } else { + reg = readl(base + EXYNOS4_INT_EN_REG) & + ~EXYNOS5433_INT_EN_MASK; + writel(reg | EXYNOS5433_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); + } } unsigned int exynos4_jpeg_get_int_status(void __iomem *base) @@ -234,6 +256,36 @@ void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, writel(reg, base + EXYNOS4_TBL_SEL_REG); } +void exynos4_jpeg_set_dec_components(void __iomem *base, int n) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_NF(n); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_Q_TBL_COMP(c, x); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x) +{ + unsigned int reg; + + reg = readl(base + EXYNOS4_TBL_SEL_REG); + + reg |= EXYNOS4_HUFF_TBL_COMP(c, x); + writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt) { if (fmt == V4L2_PIX_FMT_GREY) diff --git a/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h index c228d28a4..cf6ec055d 100644 --- a/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h +++ b/kernel/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h @@ -15,10 +15,12 @@ void exynos4_jpeg_sw_reset(void __iomem *base); void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode); -void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt); -void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt); +void __exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt, + unsigned int version); +void __exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt, + unsigned int version); void exynos4_jpeg_set_enc_tbl(void __iomem *base); -void exynos4_jpeg_set_interrupt(void __iomem *base); +void exynos4_jpeg_set_interrupt(void __iomem *base, unsigned int version); unsigned int exynos4_jpeg_get_int_status(void __iomem *base); void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value); void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value); @@ -30,6 +32,9 @@ void exynos4_jpeg_set_frame_buf_address(void __iomem *base, struct s5p_jpeg_addr *jpeg_addr); void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, enum exynos4_jpeg_img_quality_level level); +void exynos4_jpeg_set_dec_components(void __iomem *base, int n); +void exynos4_jpeg_select_dec_q_tbl(void __iomem *base, char c, char x); +void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x); void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); diff --git a/kernel/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/kernel/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 050fc4402..187040046 100644 --- a/kernel/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/kernel/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -231,12 +231,14 @@ /* JPEG INT Register bit */ #define EXYNOS4_INT_EN_MASK (0x1f << 0) +#define EXYNOS5433_INT_EN_MASK (0x1ff << 0) #define EXYNOS4_PROT_ERR_INT_EN (1 << 0) #define EXYNOS4_IMG_COMPLETION_INT_EN (1 << 1) #define EXYNOS4_DEC_INVALID_FORMAT_EN (1 << 2) #define EXYNOS4_MULTI_SCAN_ERROR_EN (1 << 3) #define EXYNOS4_FRAME_ERR_EN (1 << 4) #define EXYNOS4_INT_EN_ALL (0x1f << 0) +#define EXYNOS5433_INT_EN_ALL (0x1b6 << 0) #define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) #define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) @@ -296,6 +298,8 @@ #define EXYNOS4_ENC_FMT_SHIFT 24 #define EXYNOS4_ENC_FMT_MASK (3 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS5433_ENC_FMT_MASK (7 << EXYNOS4_ENC_FMT_SHIFT) + #define EXYNOS4_ENC_FMT_GRAY (0 << EXYNOS4_ENC_FMT_SHIFT) #define EXYNOS4_ENC_FMT_YUV_444 (1 << EXYNOS4_ENC_FMT_SHIFT) #define EXYNOS4_ENC_FMT_YUV_422 (2 << EXYNOS4_ENC_FMT_SHIFT) @@ -305,6 +309,8 @@ #define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) #define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) +#define EXYNOS5433_SWAP_CHROMA_CRCB (1 << 27) +#define EXYNOS5433_SWAP_CHROMA_CBCR (0 << 27) /* JPEG HUFF count Register bit */ #define EXYNOS4_HUFF_COUNT_MASK 0xffff @@ -316,35 +322,56 @@ #define EXYNOS4_DECODED_IMG_FMT_MASK 0x3 /* JPEG TBL SEL Register bit */ -#define EXYNOS4_Q_TBL_COMP1_0 (0 << 0) -#define EXYNOS4_Q_TBL_COMP1_1 (1 << 0) -#define EXYNOS4_Q_TBL_COMP1_2 (2 << 0) -#define EXYNOS4_Q_TBL_COMP1_3 (3 << 0) - -#define EXYNOS4_Q_TBL_COMP2_0 (0 << 2) -#define EXYNOS4_Q_TBL_COMP2_1 (1 << 2) -#define EXYNOS4_Q_TBL_COMP2_2 (2 << 2) -#define EXYNOS4_Q_TBL_COMP2_3 (3 << 2) - -#define EXYNOS4_Q_TBL_COMP3_0 (0 << 4) -#define EXYNOS4_Q_TBL_COMP3_1 (1 << 4) -#define EXYNOS4_Q_TBL_COMP3_2 (2 << 4) -#define EXYNOS4_Q_TBL_COMP3_3 (3 << 4) - -#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 (0 << 6) -#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 (1 << 6) -#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 (2 << 6) -#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 (3 << 6) - -#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 (0 << 8) -#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 (1 << 8) -#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 (2 << 8) -#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 (3 << 8) - -#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 (0 << 10) -#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 (1 << 10) -#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 (2 << 10) -#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 (3 << 10) +#define EXYNOS4_Q_TBL_COMP(c, n) ((n) << (((c) - 1) << 1)) + +#define EXYNOS4_Q_TBL_COMP1_0 EXYNOS4_Q_TBL_COMP(1, 0) +#define EXYNOS4_Q_TBL_COMP1_1 EXYNOS4_Q_TBL_COMP(1, 1) +#define EXYNOS4_Q_TBL_COMP1_2 EXYNOS4_Q_TBL_COMP(1, 2) +#define EXYNOS4_Q_TBL_COMP1_3 EXYNOS4_Q_TBL_COMP(1, 3) + +#define EXYNOS4_Q_TBL_COMP2_0 EXYNOS4_Q_TBL_COMP(2, 0) +#define EXYNOS4_Q_TBL_COMP2_1 EXYNOS4_Q_TBL_COMP(2, 1) +#define EXYNOS4_Q_TBL_COMP2_2 EXYNOS4_Q_TBL_COMP(2, 2) +#define EXYNOS4_Q_TBL_COMP2_3 EXYNOS4_Q_TBL_COMP(2, 3) + +#define EXYNOS4_Q_TBL_COMP3_0 EXYNOS4_Q_TBL_COMP(3, 0) +#define EXYNOS4_Q_TBL_COMP3_1 EXYNOS4_Q_TBL_COMP(3, 1) +#define EXYNOS4_Q_TBL_COMP3_2 EXYNOS4_Q_TBL_COMP(3, 2) +#define EXYNOS4_Q_TBL_COMP3_3 EXYNOS4_Q_TBL_COMP(3, 3) + +#define EXYNOS4_HUFF_TBL_COMP(c, n) ((n) << ((((c) - 1) << 1) + 6)) + +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(1, 0) +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(1, 1) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(1, 2) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(1, 3) + +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(2, 0) +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(2, 1) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(2, 2) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(2, 3) + +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(3, 0) +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(3, 1) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 \ + EXYNOS4_HUFF_TBL_COMP(3, 2) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 \ + EXYNOS4_HUFF_TBL_COMP(3, 3) + +#define EXYNOS4_NF_SHIFT 16 +#define EXYNOS4_NF_MASK 0xff +#define EXYNOS4_NF(x) \ + (((x) << EXYNOS4_NF_SHIFT) & EXYNOS4_NF_MASK) /* JPEG quantizer table register */ #define EXYNOS4_QTBL_CONTENT(n) (0x100 + (n) * 0x40) diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8333fbc2f..3ffe2ecfd 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" #include "s5p_mfc_debug.h" @@ -181,13 +181,6 @@ unlock: mutex_unlock(&dev->mfc_mutex); } -static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev) -{ - mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT); - mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD); - mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID); -} - static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_buf *dst_buf; @@ -199,21 +192,23 @@ static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx) dst_buf = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); mfc_debug(2, "Cleaning up buffer: %d\n", - dst_buf->b->v4l2_buf.index); - vb2_set_plane_payload(dst_buf->b, 0, 0); - vb2_set_plane_payload(dst_buf->b, 1, 0); + dst_buf->b->vb2_buf.index); + vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, 0); + vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, 0); list_del(&dst_buf->list); + dst_buf->flags |= MFC_BUF_FLAG_EOS; ctx->dst_queue_cnt--; - dst_buf->b->v4l2_buf.sequence = (ctx->sequence++); + dst_buf->b->sequence = (ctx->sequence++); if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) == s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx)) - dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE; + dst_buf->b->field = V4L2_FIELD_NONE; else - dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED; + dst_buf->b->field = V4L2_FIELD_INTERLACED; + dst_buf->b->flags |= V4L2_BUF_FLAG_LAST; - ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index); - vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE); + ctx->dec_dst_flag &= ~(1 << dst_buf->b->vb2_buf.index); + vb2_buffer_done(&dst_buf->b->vb2_buf, VB2_BUF_STATE_DONE); } } @@ -234,27 +229,28 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) appropriate flags. */ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { - dst_buf->b->v4l2_buf.timecode = - src_buf->b->v4l2_buf.timecode; - dst_buf->b->v4l2_buf.timestamp = - src_buf->b->v4l2_buf.timestamp; - dst_buf->b->v4l2_buf.flags &= + if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) + == dec_y_addr) { + dst_buf->b->timecode = + src_buf->b->timecode; + dst_buf->b->timestamp = + src_buf->b->timestamp; + dst_buf->b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->b->v4l2_buf.flags |= - src_buf->b->v4l2_buf.flags + dst_buf->b->flags |= + src_buf->b->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; switch (frame_type) { case S5P_FIMV_DECODE_FRAME_I_FRAME: - dst_buf->b->v4l2_buf.flags |= + dst_buf->b->flags |= V4L2_BUF_FLAG_KEYFRAME; break; case S5P_FIMV_DECODE_FRAME_P_FRAME: - dst_buf->b->v4l2_buf.flags |= + dst_buf->b->flags |= V4L2_BUF_FLAG_PFRAME; break; case S5P_FIMV_DECODE_FRAME_B_FRAME: - dst_buf->b->v4l2_buf.flags |= + dst_buf->b->flags |= V4L2_BUF_FLAG_BFRAME; break; default: @@ -295,25 +291,28 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) * check which videobuf does it correspond to */ list_for_each_entry(dst_buf, &ctx->dst_queue, list) { /* Check if this is the buffer we're looking for */ - if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) { + if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) + == dspl_y_addr) { list_del(&dst_buf->list); ctx->dst_queue_cnt--; - dst_buf->b->v4l2_buf.sequence = ctx->sequence; + dst_buf->b->sequence = ctx->sequence; if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) == s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx)) - dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE; + dst_buf->b->field = V4L2_FIELD_NONE; else - dst_buf->b->v4l2_buf.field = + dst_buf->b->field = V4L2_FIELD_INTERLACED; - vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size); - vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size); - clear_bit(dst_buf->b->v4l2_buf.index, + vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, + ctx->luma_size); + vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, + ctx->chroma_size); + clear_bit(dst_buf->b->vb2_buf.index, &ctx->dec_dst_flag); - vb2_buffer_done(dst_buf->b, - err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + vb2_buffer_done(&dst_buf->b->vb2_buf, err ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); break; } @@ -394,7 +393,7 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC && ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC && ctx->consumed_stream + STUFF_BYTE < - src_buf->b->v4l2_planes[0].bytesused) { + src_buf->b->vb2_buf.planes[0].bytesused) { /* Run MFC again on the same buffer */ mfc_debug(2, "Running again the same buffer\n"); ctx->after_packed_pb = 1; @@ -406,9 +405,11 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, list_del(&src_buf->list); ctx->src_queue_cnt--; if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0) - vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&src_buf->b->vb2_buf, + VB2_BUF_STATE_ERROR); else - vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE); + vb2_buffer_done(&src_buf->b->vb2_buf, + VB2_BUF_STATE_DONE); } } leave_handle_frame: @@ -509,7 +510,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf, list); if (s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream, dev) < - src_buf->b->v4l2_planes[0].bytesused) + src_buf->b->vb2_buf.planes[0].bytesused) ctx->head_processed = 0; else ctx->head_processed = 1; @@ -550,7 +551,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, struct s5p_mfc_buf, list); list_del(&src_buf->list); ctx->src_queue_cnt--; - vb2_buffer_done(src_buf->b, + vb2_buffer_done(&src_buf->b->vb2_buf, VB2_BUF_STATE_DONE); } spin_unlock_irqrestore(&dev->irqlock, flags); @@ -572,17 +573,13 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, } } -static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) +static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *mb_entry; mfc_debug(2, "Stream completed\n"); - s5p_mfc_clear_int_flags(dev); - ctx->int_type = reason; - ctx->int_err = err; ctx->state = MFCINST_FINISHED; spin_lock(&dev->irqlock); @@ -591,8 +588,8 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, list); list_del(&mb_entry->list); ctx->dst_queue_cnt--; - vb2_set_plane_payload(mb_entry->b, 0, 0); - vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0); + vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE); } spin_unlock(&dev->irqlock); @@ -639,6 +636,13 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) if (ctx->c_ops->post_frame_start) { if (ctx->c_ops->post_frame_start(ctx)) mfc_err("post_frame_start() failed\n"); + + if (ctx->state == MFCINST_FINISHING && + list_empty(&ctx->ref_queue)) { + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_handle_stream_complete(ctx); + break; + } s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); wake_up_ctx(ctx, reason, err); WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); @@ -684,7 +688,10 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) break; case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET: - s5p_mfc_handle_stream_complete(ctx, reason, err); + s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev); + ctx->int_type = reason; + ctx->int_err = err; + s5p_mfc_handle_stream_complete(ctx); break; case S5P_MFC_R2H_CMD_DPB_FLUSH_RET: @@ -1337,8 +1344,6 @@ static int s5p_mfc_runtime_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); - if (!m_dev->alloc_ctx) - return 0; atomic_set(&m_dev->pm.power, 1); return 0; } @@ -1463,7 +1468,7 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = { .fw_name[0] = "s5p-mfc-v8.fw", }; -static struct platform_device_id mfc_driver_ids[] = { +static const struct platform_device_id mfc_driver_ids[] = { { .name = "s5p-mfc", .driver_data = (unsigned long)&mfc_drvdata_v5, diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index f17609669..b1b149151 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -37,8 +37,12 @@ static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; + + ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev); + if (ret) + return ret; - s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev); mfc_write(dev, dev->ctx_buf.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6); mfc_write(dev, buf_size->dev_ctx, S5P_FIMV_CONTEXT_MEM_SIZE_V6); return s5p_mfc_cmd_host2risc_v6(dev, S5P_FIMV_H2R_CMD_SYS_INIT_V6, diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 24262bbb1..d1a3f9b1b 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "regs-mfc.h" #include "regs-mfc-v8.h" @@ -179,8 +179,8 @@ struct s5p_mfc_ctx; * struct s5p_mfc_buf - MFC buffer */ struct s5p_mfc_buf { + struct vb2_v4l2_buffer *b; struct list_head list; - struct vb2_buffer *b; union { struct { size_t luma; diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index aebe4fd7f..8c5060a75 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" #include "s5p_mfc_debug.h" @@ -645,17 +645,22 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) mfc_err("Call on DQBUF after unrecoverable error\n"); return -EIO; } - if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + + switch (buf->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - if (ret == 0 && ctx->state == MFCINST_FINISHED && - list_empty(&ctx->vq_dst.done_list)) + if (ret) + return ret; + + if (ctx->state == MFCINST_FINISHED && + (ctx->dst_bufs[buf->index].flags & MFC_BUF_FLAG_EOS)) v4l2_event_queue_fh(&ctx->fh, &ev); - } else { - ret = -EINVAL; + return 0; + default: + return -EINVAL; } - return ret; } /* Export DMA buffer */ @@ -883,7 +888,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { }; static int s5p_mfc_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *buf_count, + const void *parg, unsigned int *buf_count, unsigned int *plane_count, unsigned int psize[], void *allocators[]) { @@ -945,6 +950,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, static int s5p_mfc_buf_init(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); unsigned int i; @@ -964,8 +970,8 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) mfc_err("Plane buffer (CAPTURE) is too small\n"); return -EINVAL; } - i = vb->v4l2_buf.index; - ctx->dst_bufs[i].b = vb; + i = vb->index; + ctx->dst_bufs[i].b = vbuf; ctx->dst_bufs[i].cookie.raw.luma = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs[i].cookie.raw.chroma = @@ -982,8 +988,8 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) return -EINVAL; } - i = vb->v4l2_buf.index; - ctx->src_bufs[i].b = vb; + i = vb->index; + ctx->src_bufs[i].b = vbuf; ctx->src_bufs[i].cookie.stream = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs_cnt++; @@ -1065,18 +1071,18 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) struct s5p_mfc_buf *mfc_buf; if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; + mfc_buf = &ctx->src_bufs[vb->index]; mfc_buf->flags &= ~MFC_BUF_FLAG_USED; spin_lock_irqsave(&dev->irqlock, flags); list_add_tail(&mfc_buf->list, &ctx->src_queue); ctx->src_queue_cnt++; spin_unlock_irqrestore(&dev->irqlock, flags); } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index]; + mfc_buf = &ctx->dst_bufs[vb->index]; mfc_buf->flags &= ~MFC_BUF_FLAG_USED; /* Mark destination as available for use by MFC */ spin_lock_irqsave(&dev->irqlock, flags); - set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag); + set_bit(vb->index, &ctx->dec_dst_flag); list_add_tail(&mfc_buf->list, &ctx->dst_queue); ctx->dst_queue_cnt++; spin_unlock_irqrestore(&dev->irqlock, flags); diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index e65993f4b..5c678ec9c 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" #include "s5p_mfc_debug.h" @@ -773,8 +773,8 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -796,10 +796,11 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) struct s5p_mfc_buf, list); list_del(&dst_mb->list); ctx->dst_queue_cnt--; - vb2_set_plane_payload(dst_mb->b, 0, + vb2_set_plane_payload(&dst_mb->b->vb2_buf, 0, s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev)); - vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE); + vb2_buffer_done(&dst_mb->b->vb2_buf, + VB2_BUF_STATE_DONE); } spin_unlock_irqrestore(&dev->irqlock, flags); } @@ -831,16 +832,16 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); - src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); + src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); + src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_frame_buffer, ctx, src_y_addr, src_c_addr); spin_unlock_irqrestore(&dev->irqlock, flags); spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -869,25 +870,29 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) s5p_mfc_hw_call_void(dev->mfc_ops, get_enc_frame_buffer, ctx, &enc_y_addr, &enc_c_addr); list_for_each_entry(mb_entry, &ctx->src_queue, list) { - mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); + mb_y_addr = vb2_dma_contig_plane_dma_addr( + &mb_entry->b->vb2_buf, 0); + mb_c_addr = vb2_dma_contig_plane_dma_addr( + &mb_entry->b->vb2_buf, 1); if ((enc_y_addr == mb_y_addr) && (enc_c_addr == mb_c_addr)) { list_del(&mb_entry->list); ctx->src_queue_cnt--; - vb2_buffer_done(mb_entry->b, + vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE); break; } } list_for_each_entry(mb_entry, &ctx->ref_queue, list) { - mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); + mb_y_addr = vb2_dma_contig_plane_dma_addr( + &mb_entry->b->vb2_buf, 0); + mb_c_addr = vb2_dma_contig_plane_dma_addr( + &mb_entry->b->vb2_buf, 1); if ((enc_y_addr == mb_y_addr) && (enc_c_addr == mb_c_addr)) { list_del(&mb_entry->list); ctx->ref_queue_cnt--; - vb2_buffer_done(mb_entry->b, + vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE); break; } @@ -902,9 +907,9 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) list_add_tail(&mb_entry->list, &ctx->ref_queue); ctx->ref_queue_cnt++; } - mfc_debug(2, "enc src count: %d, enc ref count: %d\n", - ctx->src_queue_cnt, ctx->ref_queue_cnt); } + mfc_debug(2, "enc src count: %d, enc ref count: %d\n", + ctx->src_queue_cnt, ctx->ref_queue_cnt); if ((ctx->dst_queue_cnt > 0) && (strm_size > 0)) { mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); @@ -912,21 +917,22 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) ctx->dst_queue_cnt--; switch (slice_type) { case S5P_FIMV_ENC_SI_SLICE_TYPE_I: - mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + mb_entry->b->flags |= V4L2_BUF_FLAG_KEYFRAME; break; case S5P_FIMV_ENC_SI_SLICE_TYPE_P: - mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + mb_entry->b->flags |= V4L2_BUF_FLAG_PFRAME; break; case S5P_FIMV_ENC_SI_SLICE_TYPE_B: - mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME; + mb_entry->b->flags |= V4L2_BUF_FLAG_BFRAME; break; } - vb2_set_plane_payload(mb_entry->b, 0, strm_size); - vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, strm_size); + vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE); } spin_unlock_irqrestore(&dev->irqlock, flags); if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0)) clear_work_bit(ctx); + return 0; } @@ -1806,24 +1812,25 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) return -EINVAL; } mfc_debug(2, "index: %d, plane[%d] cookie: %pad\n", - vb->v4l2_buf.index, i, &dma); + vb->index, i, &dma); } return 0; } static int s5p_mfc_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *buf_count, unsigned int *plane_count, unsigned int psize[], void *allocators[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; - if (ctx->state != MFCINST_GOT_INST) { - mfc_err("inavlid state: %d\n", ctx->state); - return -EINVAL; - } if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (ctx->state != MFCINST_GOT_INST) { + mfc_err("invalid state: %d\n", ctx->state); + return -EINVAL; + } + if (ctx->dst_fmt) *plane_count = ctx->dst_fmt->num_planes; else @@ -1860,7 +1867,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX]; } } else { - mfc_err("inavlid queue type: %d\n", vq->type); + mfc_err("invalid queue type: %d\n", vq->type); return -EINVAL; } return 0; @@ -1868,6 +1875,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, static int s5p_mfc_buf_init(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); unsigned int i; @@ -1877,8 +1885,8 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) ret = check_vb_with_fmt(ctx->dst_fmt, vb); if (ret < 0) return ret; - i = vb->v4l2_buf.index; - ctx->dst_bufs[i].b = vb; + i = vb->index; + ctx->dst_bufs[i].b = vbuf; ctx->dst_bufs[i].cookie.stream = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs_cnt++; @@ -1886,15 +1894,15 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) ret = check_vb_with_fmt(ctx->src_fmt, vb); if (ret < 0) return ret; - i = vb->v4l2_buf.index; - ctx->src_bufs[i].b = vb; + i = vb->index; + ctx->src_bufs[i].b = vbuf; ctx->src_bufs[i].cookie.raw.luma = vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs[i].cookie.raw.chroma = vb2_dma_contig_plane_dma_addr(vb, 1); ctx->src_bufs_cnt++; } else { - mfc_err("inavlid queue type: %d\n", vq->type); + mfc_err("invalid queue type: %d\n", vq->type); return -EINVAL; } return 0; @@ -1930,7 +1938,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } } else { - mfc_err("inavlid queue type: %d\n", vq->type); + mfc_err("invalid queue type: %d\n", vq->type); return -EINVAL; } return 0; @@ -2011,7 +2019,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) return; } if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index]; + mfc_buf = &ctx->dst_bufs[vb->index]; mfc_buf->flags &= ~MFC_BUF_FLAG_USED; /* Mark destination as available for use by MFC */ spin_lock_irqsave(&dev->irqlock, flags); @@ -2019,7 +2027,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) ctx->dst_queue_cnt++; spin_unlock_irqrestore(&dev->irqlock, flags); } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; + mfc_buf = &ctx->src_bufs[vb->index]; mfc_buf->flags &= ~MFC_BUF_FLAG_USED; spin_lock_irqsave(&dev->irqlock, flags); list_add_tail(&mfc_buf->list, &ctx->src_queue); diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 00a1d8b2a..1e7250260 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -37,10 +37,9 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev) dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev); } -int s5p_mfc_alloc_priv_buf(struct device *dev, +int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base, struct s5p_mfc_priv_buf *b) { - mfc_debug(3, "Allocating priv: %zu\n", b->size); b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); @@ -50,6 +49,14 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, return -ENOMEM; } + if (b->dma < base) { + mfc_err("Invaling memory configuration!\n"); + mfc_err("Allocated buffer (%pad) is lower than memory base address (%pad)\n", + &b->dma, &base); + dma_free_coherent(dev, b->size, b->virt, b->dma); + return -ENOMEM; + } + mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma); return 0; } diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 22dfb3eff..77a08b19b 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -334,7 +334,7 @@ struct s5p_mfc_hw_ops { void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); void s5p_mfc_init_regs(struct s5p_mfc_dev *dev); -int s5p_mfc_alloc_priv_buf(struct device *dev, +int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base, struct s5p_mfc_priv_buf *b); void s5p_mfc_release_priv_buf(struct device *dev, struct s5p_mfc_priv_buf *b); diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index b09bcd140..873c933bc 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -41,7 +41,7 @@ static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) int ret; ctx->dsc.size = buf_size->dsc; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->dsc); if (ret) { mfc_err("Failed to allocate temporary buffer\n"); return ret; @@ -172,7 +172,8 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Allocate only if memory from bank 1 is necessary */ if (ctx->bank1.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &ctx->bank1); if (ret) { mfc_err("Failed to allocate Bank1 temporary buffer\n"); return ret; @@ -181,10 +182,11 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) } /* Allocate only if memory from bank 2 is necessary */ if (ctx->bank2.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, dev->bank2, + &ctx->bank2); if (ret) { mfc_err("Failed to allocate Bank2 temporary buffer\n"); - s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); return ret; } BUG_ON(ctx->bank2.dma & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); @@ -212,7 +214,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) else ctx->ctx.size = buf_size->non_h264_ctx; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx); if (ret) { mfc_err("Failed to allocate instance buffer\n"); return ret; @@ -225,7 +227,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) /* Initialize shared memory */ ctx->shm.size = buf_size->shm; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->shm); if (ret) { mfc_err("Failed to allocate shared memory buffer\n"); s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx); @@ -263,7 +265,7 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { - writel(data, (void *)(ctx->shm.virt + ofs)); + *(u32 *)(ctx->shm.virt + ofs) = data; wmb(); } @@ -271,7 +273,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, unsigned long ofs) { rmb(); - return readl((void *)(ctx->shm.virt + ofs)); + return *(u32 *)(ctx->shm.virt + ofs); } static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) @@ -1206,11 +1208,11 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); temp_vb->flags |= MFC_BUF_FLAG_USED; s5p_mfc_set_dec_stream_buffer_v5(ctx, - vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), - ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused); + vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), + ctx->consumed_stream, temp_vb->b->vb2_buf.planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; - if (temp_vb->b->v4l2_planes[0].bytesused == 0) { + if (temp_vb->b->vb2_buf.planes[0].bytesused == 0) { last_frame = MFC_DEC_LAST_FRAME; mfc_debug(2, "Setting ctx->state to FINISHING\n"); ctx->state = MFCINST_FINISHING; @@ -1247,16 +1249,16 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_mb->flags |= MFC_BUF_FLAG_USED; - if (src_mb->b->v4l2_planes[0].bytesused == 0) { + if (src_mb->b->vb2_buf.planes[0].bytesused == 0) { /* send null frame */ s5p_mfc_set_enc_frame_buffer_v5(ctx, dev->bank2, dev->bank2); ctx->state = MFCINST_FINISHING; } else { - src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, - 0); - src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, - 1); + src_y_addr = vb2_dma_contig_plane_dma_addr( + &src_mb->b->vb2_buf, 0); + src_c_addr = vb2_dma_contig_plane_dma_addr( + &src_mb->b->vb2_buf, 1); s5p_mfc_set_enc_frame_buffer_v5(ctx, src_y_addr, src_c_addr); if (src_mb->flags & MFC_BUF_FLAG_EOS) @@ -1265,13 +1267,13 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) } dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_mb->flags |= MFC_BUF_FLAG_USED; - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; mfc_debug(2, "encoding buffer with index=%d state=%d\n", - src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state); + src_mb ? src_mb->b->vb2_buf.index : -1, ctx->state); s5p_mfc_encode_one_frame_v5(ctx); return 0; } @@ -1287,10 +1289,11 @@ static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Preparing to init decoding\n"); temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); s5p_mfc_set_dec_desc_buffer(ctx); - mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused); + mfc_debug(2, "Header size: %d\n", + temp_vb->b->vb2_buf.planes[0].bytesused); s5p_mfc_set_dec_stream_buffer_v5(ctx, - vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), - 0, temp_vb->b->v4l2_planes[0].bytesused); + vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), + 0, temp_vb->b->vb2_buf.planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; s5p_mfc_init_decode_v5(ctx); @@ -1307,8 +1310,8 @@ static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_ref_buffer_v5(ctx); spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; @@ -1340,10 +1343,11 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) return -EIO; } temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused); + mfc_debug(2, "Header size: %d\n", + temp_vb->b->vb2_buf.planes[0].bytesused); s5p_mfc_set_dec_stream_buffer_v5(ctx, - vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), - 0, temp_vb->b->v4l2_planes[0].bytesused); + vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), + 0, temp_vb->b->vb2_buf.planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; ret = s5p_mfc_set_dec_frame_buffer_v5(ctx); @@ -1476,9 +1480,9 @@ static void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq) while (!list_empty(lh)) { b = list_entry(lh->next, struct s5p_mfc_buf, list); - for (i = 0; i < b->b->num_planes; i++) - vb2_set_plane_payload(b->b, i, 0); - vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR); + for (i = 0; i < b->b->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&b->b->vb2_buf, i, 0); + vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR); list_del(&b->list); } } diff --git a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index cefad184f..b95845347 100644 --- a/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/kernel/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -239,7 +239,8 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) /* Allocate only if memory from bank 1 is necessary */ if (ctx->bank1.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &ctx->bank1); if (ret) { mfc_err("Failed to allocate Bank1 memory\n"); return ret; @@ -291,7 +292,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; } - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx); if (ret) { mfc_err("Failed to allocate instance buffer\n"); return ret; @@ -320,7 +321,8 @@ static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) mfc_debug_enter(); dev->ctx_buf.size = buf_size->dev_ctx; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &dev->ctx_buf); if (ret) { mfc_err("Failed to allocate device context buffer\n"); return ret; @@ -520,7 +522,7 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, writel(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */ writel(size, mfc_regs->e_stream_buffer_size); - mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n", + mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%x\n", addr, size); return 0; @@ -552,7 +554,7 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, enc_recon_y_addr = readl(mfc_regs->e_recon_luma_dpb_addr); enc_recon_c_addr = readl(mfc_regs->e_recon_chroma_dpb_addr); - mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr); + mfc_debug(2, "recon y addr: 0x%08lx y_addr: 0x%08lx\n", enc_recon_y_addr, *y_addr); mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); } @@ -1481,6 +1483,7 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; + int cmd; mfc_debug(2, "++\n"); @@ -1491,9 +1494,13 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) s5p_mfc_set_slice_mode(ctx); + if (ctx->state != MFCINST_FINISHING) + cmd = S5P_FIMV_CH_FRAME_START_V6; + else + cmd = S5P_FIMV_CH_LAST_FRAME_V6; + writel(ctx->inst_no, mfc_regs->instance_id); - s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, - S5P_FIMV_CH_FRAME_START_V6, NULL); + s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, cmd, NULL); mfc_debug(2, "--\n"); @@ -1560,13 +1567,13 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); temp_vb->flags |= MFC_BUF_FLAG_USED; s5p_mfc_set_dec_stream_buffer_v6(ctx, - vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), + vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), ctx->consumed_stream, - temp_vb->b->v4l2_planes[0].bytesused); + temp_vb->b->vb2_buf.planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; - if (temp_vb->b->v4l2_planes[0].bytesused == 0) { + if (temp_vb->b->vb2_buf.planes[0].bytesused == 0) { last_frame = 1; mfc_debug(2, "Setting ctx->state to FINISHING\n"); ctx->state = MFCINST_FINISHING; @@ -1590,7 +1597,7 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); - if (list_empty(&ctx->src_queue)) { + if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) { mfc_debug(2, "no src buffers.\n"); spin_unlock_irqrestore(&dev->irqlock, flags); return -EAGAIN; @@ -1602,20 +1609,33 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) return -EAGAIN; } - src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - src_mb->flags |= MFC_BUF_FLAG_USED; - src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); - src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); + if (list_empty(&ctx->src_queue)) { + /* send null frame */ + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + src_mb = NULL; + } else { + src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); + src_mb->flags |= MFC_BUF_FLAG_USED; + if (src_mb->b->vb2_buf.planes[0].bytesused == 0) { + s5p_mfc_set_enc_frame_buffer_v6(ctx, 0, 0); + ctx->state = MFCINST_FINISHING; + } else { + src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0); + src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1); - mfc_debug(2, "enc src y addr: 0x%08lx\n", src_y_addr); - mfc_debug(2, "enc src c addr: 0x%08lx\n", src_c_addr); + mfc_debug(2, "enc src y addr: 0x%08lx\n", src_y_addr); + mfc_debug(2, "enc src c addr: 0x%08lx\n", src_c_addr); - s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr); + s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr); + if (src_mb->flags & MFC_BUF_FLAG_EOS) + ctx->state = MFCINST_FINISHING; + } + } dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_mb->flags |= MFC_BUF_FLAG_USED; - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_set_enc_stream_buffer_v6(ctx, dst_addr, dst_size); @@ -1637,10 +1657,10 @@ static inline void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); mfc_debug(2, "Preparing to init decoding.\n"); temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused); + mfc_debug(2, "Header size: %d\n", temp_vb->b->vb2_buf.planes[0].bytesused); s5p_mfc_set_dec_stream_buffer_v6(ctx, - vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), 0, - temp_vb->b->v4l2_planes[0].bytesused); + vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), 0, + temp_vb->b->vb2_buf.planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; s5p_mfc_init_decode_v6(ctx); @@ -1657,8 +1677,8 @@ static inline void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); - dst_size = vb2_plane_size(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0); s5p_mfc_set_enc_stream_buffer_v6(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; @@ -1734,7 +1754,7 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) mfc_debug(1, "New context: %d\n", new_ctx); ctx = dev->ctx[new_ctx]; - mfc_debug(1, "Seting new context to %p\n", ctx); + mfc_debug(1, "Setting new context to %p\n", ctx); /* Got context to run in ctx */ mfc_debug(1, "ctx->dst_queue_cnt=%d ctx->dpb_count=%d ctx->src_queue_cnt=%d\n", ctx->dst_queue_cnt, ctx->pb_count, ctx->src_queue_cnt); @@ -1834,9 +1854,9 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) while (!list_empty(lh)) { b = list_entry(lh->next, struct s5p_mfc_buf, list); - for (i = 0; i < b->b->num_planes; i++) - vb2_set_plane_payload(b->b, i, 0); - vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR); + for (i = 0; i < b->b->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&b->b->vb2_buf, i, 0); + vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR); list_del(&b->list); } } @@ -1852,7 +1872,7 @@ static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { s5p_mfc_clock_on(); - writel(data, (void *)((unsigned long)ofs)); + writel(data, (void __iomem *)((unsigned long)ofs)); s5p_mfc_clock_off(); } @@ -1862,7 +1882,7 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned long ofs) int ret; s5p_mfc_clock_on(); - ret = readl((void *)ofs); + ret = readl((void __iomem *)ofs); s5p_mfc_clock_off(); return ret; diff --git a/kernel/drivers/media/platform/s5p-tv/hdmi_drv.c b/kernel/drivers/media/platform/s5p-tv/hdmi_drv.c index 0e74aabf5..79940757b 100644 --- a/kernel/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/kernel/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -96,7 +96,7 @@ struct hdmi_device { struct hdmi_resources res; }; -static struct platform_device_id hdmi_driver_types[] = { +static const struct platform_device_id hdmi_driver_types[] = { { .name = "s5pv210-hdmi", }, { @@ -648,15 +648,20 @@ static int hdmi_g_dv_timings(struct v4l2_subdev *sd, return 0; } -static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int hdmi_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; struct hdmi_device *hdev = sd_to_hdmi_dev(sd); const struct hdmi_timings *t = hdev->cur_conf; dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; + if (format->pad) + return -EINVAL; + memset(fmt, 0, sizeof(*fmt)); fmt->width = t->hact.end - t->hact.beg; fmt->height = t->vact[0].end - t->vact[0].beg; @@ -712,18 +717,19 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { .s_dv_timings = hdmi_s_dv_timings, .g_dv_timings = hdmi_g_dv_timings, - .g_mbus_fmt = hdmi_g_mbus_fmt, .s_stream = hdmi_s_stream, }; static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = { .enum_dv_timings = hdmi_enum_dv_timings, .dv_timings_cap = hdmi_dv_timings_cap, + .get_fmt = hdmi_get_fmt, }; static const struct v4l2_subdev_ops hdmi_sd_ops = { .core = &hdmi_sd_core_ops, .video = &hdmi_sd_video_ops, + .pad = &hdmi_sd_pad_ops, }; static int hdmi_runtime_suspend(struct device *dev) diff --git a/kernel/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/kernel/drivers/media/platform/s5p-tv/hdmiphy_drv.c index c2f2e3564..aae652351 100644 --- a/kernel/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/kernel/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(i2c, hdmiphy_id); static struct i2c_driver hdmiphy_driver = { .driver = { .name = "s5p-hdmiphy", - .owner = THIS_MODULE, }, .probe = hdmiphy_probe, .remove = hdmiphy_remove, diff --git a/kernel/drivers/media/platform/s5p-tv/mixer.h b/kernel/drivers/media/platform/s5p-tv/mixer.h index fb2acc531..42cd2709c 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer.h +++ b/kernel/drivers/media/platform/s5p-tv/mixer.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "regs-mixer.h" @@ -113,7 +113,7 @@ struct mxr_geometry { /** instance of a buffer */ struct mxr_buffer { /** common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; /** node for layer's lists */ struct list_head list; }; diff --git a/kernel/drivers/media/platform/s5p-tv/mixer_drv.c b/kernel/drivers/media/platform/s5p-tv/mixer_drv.c index 2a9501d7e..5ef677749 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/kernel/drivers/media/platform/s5p-tv/mixer_drv.c @@ -46,11 +46,15 @@ void mxr_get_mbus_fmt(struct mxr_device *mdev, struct v4l2_mbus_framefmt *mbus_fmt) { struct v4l2_subdev *sd; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; mutex_lock(&mdev->mutex); sd = to_outsd(mdev); - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + *mbus_fmt = fmt.format; WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); mutex_unlock(&mdev->mutex); } @@ -62,7 +66,10 @@ void mxr_streamer_get(struct mxr_device *mdev) mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); if (mdev->n_streamer == 1) { struct v4l2_subdev *sd = to_outsd(mdev); - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; struct mxr_resources *res = &mdev->res; int ret; @@ -72,12 +79,12 @@ void mxr_streamer_get(struct mxr_device *mdev) clk_set_parent(res->sclk_mixer, res->sclk_hdmi); mxr_reg_s_output(mdev, to_output(mdev)->cookie); - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); ret = v4l2_subdev_call(sd, video, s_stream, 1); WARN(ret, "starting stream failed for output %s\n", sd->name); - mxr_reg_set_mbus_fmt(mdev, &mbus_fmt); + mxr_reg_set_mbus_fmt(mdev, mbus_fmt); mxr_reg_streamon(mdev); ret = mxr_reg_wait4vsync(mdev); WARN(ret, "failed to get vsync (%d) from output\n", ret); diff --git a/kernel/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/kernel/drivers/media/platform/s5p-tv/mixer_grp_layer.c index 74344c764..db3163b23 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer_grp_layer.c +++ b/kernel/drivers/media/platform/s5p-tv/mixer_grp_layer.c @@ -86,7 +86,7 @@ static void mxr_graph_buffer_set(struct mxr_layer *layer, dma_addr_t addr = 0; if (buf) - addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); mxr_reg_graph_buffer(layer->mdev, layer->idx, addr); } diff --git a/kernel/drivers/media/platform/s5p-tv/mixer_reg.c b/kernel/drivers/media/platform/s5p-tv/mixer_reg.c index b71340302..a0ec14a1d 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/kernel/drivers/media/platform/s5p-tv/mixer_reg.c @@ -279,7 +279,7 @@ static void mxr_irq_layer_handle(struct mxr_layer *layer) layer->ops.buffer_set(layer, layer->update_buf); if (done && done != layer->shadow_buf) - vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&done->vb.vb2_buf, VB2_BUF_STATE_DONE); done: spin_unlock(&layer->enq_slock); @@ -357,17 +357,15 @@ void mxr_reg_streamoff(struct mxr_device *mdev) int mxr_reg_wait4vsync(struct mxr_device *mdev) { - int ret; + long time_left; clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags); /* TODO: consider adding interruptible */ - ret = wait_event_timeout(mdev->event_queue, - test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), - msecs_to_jiffies(1000)); - if (ret > 0) + time_left = wait_event_timeout(mdev->event_queue, + test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), + msecs_to_jiffies(1000)); + if (time_left > 0) return 0; - if (ret < 0) - return ret; mxr_warn(mdev, "no vsync detected - timeout\n"); return -ETIME; } diff --git a/kernel/drivers/media/platform/s5p-tv/mixer_video.c b/kernel/drivers/media/platform/s5p-tv/mixer_video.c index 751f3b618..dc1c679e1 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer_video.c +++ b/kernel/drivers/media/platform/s5p-tv/mixer_video.c @@ -881,7 +881,7 @@ static const struct v4l2_file_operations mxr_fops = { .unlocked_ioctl = video_ioctl2, }; -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -914,7 +914,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, static void buf_queue(struct vb2_buffer *vb) { - struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mxr_buffer *buffer = container_of(vbuf, struct mxr_buffer, vb); struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); struct mxr_device *mdev = layer->mdev; unsigned long flags; @@ -963,11 +964,13 @@ static void mxr_watchdog(unsigned long arg) if (layer->update_buf == layer->shadow_buf) layer->update_buf = NULL; if (layer->update_buf) { - vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&layer->update_buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); layer->update_buf = NULL; } if (layer->shadow_buf) { - vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&layer->shadow_buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); layer->shadow_buf = NULL; } spin_unlock_irqrestore(&layer->enq_slock, flags); @@ -991,7 +994,7 @@ static void stop_streaming(struct vb2_queue *vq) /* set all buffer to be done */ list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&layer->enq_slock, flags); diff --git a/kernel/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/kernel/drivers/media/platform/s5p-tv/mixer_vp_layer.c index c9388c45a..dd002a497 100644 --- a/kernel/drivers/media/platform/s5p-tv/mixer_vp_layer.c +++ b/kernel/drivers/media/platform/s5p-tv/mixer_vp_layer.c @@ -97,9 +97,10 @@ static void mxr_vp_buffer_set(struct mxr_layer *layer, mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); return; } - luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 0); + luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); if (layer->fmt->num_subframes == 2) { - chroma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 1); + chroma_addr[0] = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1); } else { /* FIXME: mxr_get_plane_size compute integer division, * which is slow and should not be performed in interrupt */ diff --git a/kernel/drivers/media/platform/s5p-tv/sdo_drv.c b/kernel/drivers/media/platform/s5p-tv/sdo_drv.c index 3621af91d..c75d4354d 100644 --- a/kernel/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/kernel/drivers/media/platform/s5p-tv/sdo_drv.c @@ -160,13 +160,17 @@ static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std) return 0; } -static int sdo_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int sdo_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; struct sdo_device *sdev = sd_to_sdev(sd); if (!sdev->fmt) return -ENXIO; + if (format->pad) + return -EINVAL; /* all modes are 720 pixels wide */ fmt->width = 720; fmt->height = sdev->fmt->height; @@ -256,13 +260,17 @@ static const struct v4l2_subdev_video_ops sdo_sd_video_ops = { .s_std_output = sdo_s_std_output, .g_std_output = sdo_g_std_output, .g_tvnorms_output = sdo_g_tvnorms_output, - .g_mbus_fmt = sdo_g_mbus_fmt, .s_stream = sdo_s_stream, }; +static const struct v4l2_subdev_pad_ops sdo_sd_pad_ops = { + .get_fmt = sdo_get_fmt, +}; + static const struct v4l2_subdev_ops sdo_sd_ops = { .core = &sdo_sd_core_ops, .video = &sdo_sd_video_ops, + .pad = &sdo_sd_pad_ops, }; static int sdo_runtime_suspend(struct device *dev) diff --git a/kernel/drivers/media/platform/s5p-tv/sii9234_drv.c b/kernel/drivers/media/platform/s5p-tv/sii9234_drv.c index db8c17bb4..8d171310a 100644 --- a/kernel/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/kernel/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -397,7 +397,6 @@ MODULE_DEVICE_TABLE(i2c, sii9234_id); static struct i2c_driver sii9234_driver = { .driver = { .name = "sii9234", - .owner = THIS_MODULE, .pm = &sii9234_pm_ops, }, .probe = sii9234_probe, diff --git a/kernel/drivers/media/platform/sh_veu.c b/kernel/drivers/media/platform/sh_veu.c index 2554f3719..d6ab33e70 100644 --- a/kernel/drivers/media/platform/sh_veu.c +++ b/kernel/drivers/media/platform/sh_veu.c @@ -211,7 +211,7 @@ static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV24: - return V4L2_COLORSPACE_JPEG; + return V4L2_COLORSPACE_SMPTE170M; case V4L2_PIX_FMT_RGB332: case V4L2_PIX_FMT_RGB444: case V4L2_PIX_FMT_RGB565: @@ -865,10 +865,11 @@ static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { /* ========== Queue operations ========== */ static int sh_veu_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *f, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *f = parg; struct sh_veu_dev *veu = vb2_get_drv_priv(vq); struct sh_veu_vfmt *vfmt; unsigned int size, count = *nbuffers; @@ -931,9 +932,10 @@ static int sh_veu_buf_prepare(struct vb2_buffer *vb) static void sh_veu_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); - dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type); - v4l2_m2m_buf_queue(veu->m2m_ctx, vb); + dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->type); + v4l2_m2m_buf_queue(veu->m2m_ctx, vbuf); } static const struct vb2_ops sh_veu_qops = { @@ -958,6 +960,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &sh_veu_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->lock = &veu->fop_lock; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret < 0) @@ -971,6 +974,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &sh_veu_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->lock = &veu->fop_lock; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1082,8 +1086,8 @@ static irqreturn_t sh_veu_bh(int irq, void *dev_id) static irqreturn_t sh_veu_isr(int irq, void *dev_id) { struct sh_veu_dev *veu = dev_id; - struct vb2_buffer *dst; - struct vb2_buffer *src; + struct vb2_v4l2_buffer *dst; + struct vb2_v4l2_buffer *src; u32 status = sh_veu_reg_read(veu, VEU_EVTR); /* bundle read mode not used */ @@ -1103,6 +1107,12 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id) if (!src || !dst) return IRQ_NONE; + dst->timestamp = src->timestamp; + dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->flags |= + src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->timecode = src->timecode; + spin_lock(&veu->lock); v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); diff --git a/kernel/drivers/media/platform/sh_vou.c b/kernel/drivers/media/platform/sh_vou.c index dde1ccc73..2231f8922 100644 --- a/kernel/drivers/media/platform/sh_vou.c +++ b/kernel/drivers/media/platform/sh_vou.c @@ -27,7 +27,8 @@ #include #include #include -#include +#include +#include /* Mirror addresses are not available for all registers */ #define VOUER 0 @@ -57,31 +58,41 @@ enum sh_vou_status { SH_VOU_RUNNING, }; +#define VOU_MIN_IMAGE_WIDTH 16 #define VOU_MAX_IMAGE_WIDTH 720 -#define VOU_MAX_IMAGE_HEIGHT 576 +#define VOU_MIN_IMAGE_HEIGHT 16 + +struct sh_vou_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +static inline struct +sh_vou_buffer *to_sh_vou_buffer(struct vb2_v4l2_buffer *vb2) +{ + return container_of(vb2, struct sh_vou_buffer, vb); +} struct sh_vou_device { struct v4l2_device v4l2_dev; struct video_device vdev; - atomic_t use_count; struct sh_vou_pdata *pdata; spinlock_t lock; void __iomem *base; /* State information */ struct v4l2_pix_format pix; struct v4l2_rect rect; - struct list_head queue; + struct list_head buf_list; v4l2_std_id std; int pix_idx; - struct videobuf_buffer *active; + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + struct sh_vou_buffer *active; enum sh_vou_status status; + unsigned sequence; struct mutex fop_lock; }; -struct sh_vou_file { - struct videobuf_queue vbq; -}; - /* Register access routines for sides A, B and mirror addresses */ static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg, u32 value) @@ -133,6 +144,7 @@ struct sh_vou_fmt { u32 pfmt; char *desc; unsigned char bpp; + unsigned char bpl; unsigned char rgb; unsigned char yf; unsigned char pkf; @@ -143,6 +155,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_NV12, .bpp = 12, + .bpl = 1, .desc = "YVU420 planar", .yf = 0, .rgb = 0, @@ -150,6 +163,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_NV16, .bpp = 16, + .bpl = 1, .desc = "YVYU planar", .yf = 1, .rgb = 0, @@ -157,6 +171,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB24, .bpp = 24, + .bpl = 3, .desc = "RGB24", .pkf = 2, .rgb = 1, @@ -164,6 +179,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB565, .bpp = 16, + .bpl = 2, .desc = "RGB565", .pkf = 3, .rgb = 1, @@ -171,6 +187,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB565X, .bpp = 16, + .bpl = 2, .desc = "RGB565 byteswapped", .pkf = 3, .rgb = 1, @@ -178,11 +195,11 @@ static struct sh_vou_fmt vou_fmt[] = { }; static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, - struct videobuf_buffer *vb) + struct vb2_v4l2_buffer *vbuf) { dma_addr_t addr1, addr2; - addr1 = videobuf_to_dma_contig(vb); + addr1 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); switch (vou_dev->pix.pixelformat) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: @@ -196,8 +213,7 @@ static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2); } -static void sh_vou_stream_start(struct sh_vou_device *vou_dev, - struct videobuf_buffer *vb) +static void sh_vou_stream_config(struct sh_vou_device *vou_dev) { unsigned int row_coeff; #ifdef __LITTLE_ENDIAN @@ -224,167 +240,139 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap); sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff); - sh_vou_schedule_next(vou_dev, vb); -} - -static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - BUG_ON(in_interrupt()); - - /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */ - videobuf_waiton(vq, vb, 0, 0); - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; } /* Locking: caller holds fop_lock mutex */ -static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int sh_vou_queue_setup(struct vb2_queue *vq, const void *parg, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); - - *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width * - vou_dev->pix.height / 8; - - if (*count < 2) - *count = 2; - - /* Taking into account maximum frame size, *count will stay >= 2 */ - if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) - *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); + const struct v4l2_format *fmt = parg; + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix = &vou_dev->pix; + int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__, - *count, *size); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + if (fmt && fmt->fmt.pix.sizeimage < pix->height * bytes_per_line) + return -EINVAL; + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : pix->height * bytes_per_line; + alloc_ctxs[0] = vou_dev->alloc_ctx; return 0; } -/* Locking: caller holds fop_lock mutex */ -static int sh_vou_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int sh_vou_buf_prepare(struct vb2_buffer *vb) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); struct v4l2_pix_format *pix = &vou_dev->pix; - int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; - int ret; + unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; + unsigned size = pix->height * bytes_per_line; dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (vb->width != pix->width || - vb->height != pix->height || - vb->field != pix->field) { - vb->width = pix->width; - vb->height = pix->height; - vb->field = field; - if (vb->state != VIDEOBUF_NEEDS_INIT) - free_buffer(vq, vb); - } - - vb->size = vb->height * bytes_per_line; - if (vb->baddr && vb->bsize < vb->size) { + if (vb2_plane_size(vb, 0) < size) { /* User buffer too small */ - dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n", - vb->bsize, vb->baddr); + dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb, 0), size); return -EINVAL; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret < 0) { - dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n", - vb->memory, ret); - return ret; - } - vb->state = VIDEOBUF_PREPARED; - } - - dev_dbg(vou_dev->v4l2_dev.dev, - "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n", - __func__, vou_dev->pix_idx, bytes_per_line, - ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }), - vb->memory, vb->state); - + vb2_set_plane_payload(vb, 0, size); return 0; } /* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */ -static void sh_vou_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_vou_buf_queue(struct vb2_buffer *vb) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); + struct sh_vou_buffer *shbuf = to_sh_vou_buffer(vbuf); + unsigned long flags; - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + spin_lock_irqsave(&vou_dev->lock, flags); + list_add_tail(&shbuf->list, &vou_dev->buf_list); + spin_unlock_irqrestore(&vou_dev->lock, flags); +} + +static int sh_vou_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct sh_vou_buffer *buf, *node; + int ret; - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &vou_dev->queue); - - if (vou_dev->status == SH_VOU_RUNNING) { - return; - } else if (!vou_dev->active) { - vou_dev->active = vb; - /* Start from side A: we use mirror addresses, so, set B */ - sh_vou_reg_a_write(vou_dev, VOURPR, 1); - dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", - __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); - sh_vou_schedule_next(vou_dev, vb); - /* Only activate VOU after the second buffer */ - } else if (vou_dev->active->queue.next == &vb->queue) { - /* Second buffer - initialise register side B */ - sh_vou_reg_a_write(vou_dev, VOURPR, 0); - sh_vou_stream_start(vou_dev, vb); - - /* Register side switching with frame VSYNC */ - sh_vou_reg_a_write(vou_dev, VOURCR, 5); - dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n", - __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); - - /* Enable End-of-Frame (VSYNC) interrupts */ - sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); - /* Two buffers on the queue - activate the hardware */ - - vou_dev->status = SH_VOU_RUNNING; - sh_vou_reg_a_write(vou_dev, VOUER, 0x107); + vou_dev->sequence = 0; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + list_del(&buf->list); + } + vou_dev->active = NULL; + return ret; } + + buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list); + + vou_dev->active = buf; + + /* Start from side A: we use mirror addresses, so, set B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 1); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); + sh_vou_schedule_next(vou_dev, &buf->vb); + + buf = list_entry(buf->list.next, struct sh_vou_buffer, list); + + /* Second buffer - initialise register side B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 0); + sh_vou_schedule_next(vou_dev, &buf->vb); + + /* Register side switching with frame VSYNC */ + sh_vou_reg_a_write(vou_dev, VOURCR, 5); + + sh_vou_stream_config(vou_dev); + /* Enable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); + + /* Two buffers on the queue - activate the hardware */ + vou_dev->status = SH_VOU_RUNNING; + sh_vou_reg_a_write(vou_dev, VOUER, 0x107); + return 0; } -static void sh_vou_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_vou_stop_streaming(struct vb2_queue *vq) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct sh_vou_buffer *buf, *node; unsigned long flags; - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 0); + /* disable output */ + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + /* ...but the current frame will complete */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + msleep(50); spin_lock_irqsave(&vou_dev->lock, flags); - - if (vou_dev->active == vb) { - /* disable output */ - sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); - /* ...but the current frame will complete */ - sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); - vou_dev->active = NULL; + list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&buf->list); } - - if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) { - vb->state = VIDEOBUF_ERROR; - list_del(&vb->queue); - } - + vou_dev->active = NULL; spin_unlock_irqrestore(&vou_dev->lock, flags); - - free_buffer(vq, vb); } -static struct videobuf_queue_ops sh_vou_video_qops = { - .buf_setup = sh_vou_buf_setup, - .buf_prepare = sh_vou_buf_prepare, - .buf_queue = sh_vou_buf_queue, - .buf_release = sh_vou_buf_release, +static struct vb2_ops sh_vou_qops = { + .queue_setup = sh_vou_queue_setup, + .buf_prepare = sh_vou_buf_prepare, + .buf_queue = sh_vou_buf_queue, + .start_streaming = sh_vou_start_streaming, + .stop_streaming = sh_vou_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* Video IOCTLs */ @@ -396,7 +384,10 @@ static int sh_vou_querycap(struct file *file, void *priv, dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "sh-vou", sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -540,8 +531,10 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) img_height_max = 576; /* Image width must be a multiple of 4 */ - v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &geo->in_height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&geo->in_width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2, + &geo->in_height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); /* Select scales to come as close as possible to the output image */ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { @@ -600,7 +593,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { unsigned int best_err = UINT_MAX, best = geo->in_width, width_max, height_max, img_height_max; - int i, idx = 0; + int i, idx_h = 0, idx_v = 0; if (std & V4L2_STD_525_60) { width_max = 858; @@ -625,7 +618,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) err = abs(found - geo->output.width); if (err < best_err) { best_err = err; - idx = i; + idx_h = i; best = found; } if (!err) @@ -633,12 +626,12 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) } geo->output.width = best; - geo->scale_idx_h = idx; + geo->scale_idx_h = idx_h; if (geo->output.left + best > width_max) geo->output.left = width_max - best; pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width, - vou_scale_h_num[idx], vou_scale_h_den[idx], best); + vou_scale_h_num[idx_h], vou_scale_h_den[idx_h], best); best_err = UINT_MAX; @@ -655,7 +648,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) err = abs(found - geo->output.height); if (err < best_err) { best_err = err; - idx = i; + idx_v = i; best = found; } if (!err) @@ -663,40 +656,27 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) } geo->output.height = best; - geo->scale_idx_v = idx; + geo->scale_idx_v = idx_v; if (geo->output.top + best > height_max) geo->output.top = height_max - best; pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height, - vou_scale_v_num[idx], vou_scale_v_den[idx], best); + vou_scale_v_num[idx_v], vou_scale_v_den[idx_v], best); } -static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *fmt) +static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) { struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; unsigned int img_height_max; int pix_idx; - struct sh_vou_geometry geo; - struct v4l2_mbus_framefmt mbfmt = { - /* Revisit: is this the correct code? */ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .field = V4L2_FIELD_INTERLACED, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - }; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, - vou_dev->rect.width, vou_dev->rect.height, - pix->width, pix->height); - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || - pix->field != V4L2_FIELD_NONE) - return -EINVAL; + pix->field = V4L2_FIELD_INTERLACED; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->ycbcr_enc = pix->quantization = 0; for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) if (vou_fmt[pix_idx].pfmt == pix->pixelformat) @@ -710,9 +690,38 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, else img_height_max = 576; - /* Image width must be a multiple of 4 */ - v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &pix->height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&pix->width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2, + &pix->height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); + pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl; + pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3); + + return 0; +} + +static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev, + struct v4l2_pix_format *pix) +{ + unsigned int img_height_max; + struct sh_vou_geometry geo; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + /* Revisit: is this the correct code? */ + .format.code = MEDIA_BUS_FMT_YUYV8_2X8, + .format.field = V4L2_FIELD_INTERLACED, + .format.colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + struct v4l2_mbus_framefmt *mbfmt = &format.format; + int pix_idx; + int ret; + + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; + + for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) + if (vou_fmt[pix_idx].pfmt == pix->pixelformat) + break; geo.in_width = pix->width; geo.in_height = pix->height; @@ -720,27 +729,32 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, vou_adjust_output(&geo, vou_dev->std); - mbfmt.width = geo.output.width; - mbfmt.height = geo.output.height; - ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_mbus_fmt, &mbfmt); + mbfmt->width = geo.output.width; + mbfmt->height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, + set_fmt, NULL, &format); /* Must be implemented, so, don't check for -ENOIOCTLCMD */ if (ret < 0) return ret; dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, - geo.output.width, geo.output.height, mbfmt.width, mbfmt.height); + geo.output.width, geo.output.height, mbfmt->width, mbfmt->height); + + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; /* Sanity checks */ - if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > img_height_max || - mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8) + if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH || + (unsigned)mbfmt->height > img_height_max || + mbfmt->code != MEDIA_BUS_FMT_YUYV8_2X8) return -EIO; - if (mbfmt.width != geo.output.width || - mbfmt.height != geo.output.height) { - geo.output.width = mbfmt.width; - geo.output.height = mbfmt.height; + if (mbfmt->width != geo.output.width || + mbfmt->height != geo.output.height) { + geo.output.width = mbfmt->width; + geo.output.height = mbfmt->height; vou_adjust_input(&geo, vou_dev->std); } @@ -763,109 +777,39 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, return 0; } -static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *fmt) +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int i; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - pix->field = V4L2_FIELD_NONE; + int ret = sh_vou_try_fmt_vid_out(file, priv, fmt); - v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); - - for (i = 0; i < ARRAY_SIZE(vou_fmt); i++) - if (vou_fmt[i].pfmt == pix->pixelformat) - return 0; - - pix->pixelformat = vou_fmt[0].pfmt; - - return 0; + if (ret) + return ret; + return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix); } -static int sh_vou_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) +static int sh_vou_enum_output(struct file *file, void *fh, + struct v4l2_output *a) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (a->index) return -EINVAL; - - return videobuf_reqbufs(&vou_file->vbq, req); -} - -static int sh_vou_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_querybuf(&vou_file->vbq, b); + strlcpy(a->name, "Video Out", sizeof(a->name)); + a->type = V4L2_OUTPUT_TYPE_ANALOG; + a->std = vou_dev->vdev.tvnorms; + return 0; } -static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +static int sh_vou_g_output(struct file *file, void *fh, unsigned int *i) { - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_qbuf(&vou_file->vbq, b); + *i = 0; + return 0; } -static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +static int sh_vou_s_output(struct file *file, void *fh, unsigned int i) { - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); -} - -static int sh_vou_streamon(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, - video, s_stream, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - /* This calls our .buf_queue() (== sh_vou_buf_queue) */ - return videobuf_streamon(&vou_file->vbq); -} - -static int sh_vou_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - /* - * This calls buf_release from host driver's videobuf_queue_ops for all - * remaining buffers. When the last buffer is freed, stop streaming - */ - videobuf_streamoff(&vou_file->vbq); - v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0); - - return 0; + return i ? -EINVAL : 0; } static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) @@ -890,8 +834,11 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); - if (std_id & ~vou_dev->vdev.tvnorms) - return -EINVAL; + if (std_id == vou_dev->std) + return 0; + + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_std_output, std_id); @@ -899,13 +846,25 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - if (std_id & V4L2_STD_525_60) + vou_dev->rect.top = vou_dev->rect.left = 0; + vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH; + if (std_id & V4L2_STD_525_60) { sh_vou_reg_ab_set(vou_dev, VOUCR, sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); - else + vou_dev->rect.height = 480; + } else { sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); + vou_dev->rect.height = 576; + } + vou_dev->pix.width = vou_dev->rect.width; + vou_dev->pix.height = vou_dev->rect.height; + vou_dev->pix.bytesperline = + vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl; + vou_dev->pix.sizeimage = vou_dev->pix.height * + ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3); vou_dev->std = std_id; + sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); return 0; } @@ -921,49 +880,95 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) +static int sh_vou_log_status(struct file *file, void *priv) { struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + pr_info("VOUER: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER)); + pr_info("VOUCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR)); + pr_info("VOUSTR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR)); + pr_info("VOUVCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR)); + pr_info("VOUISR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR)); + pr_info("VOUBCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR)); + pr_info("VOUDPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR)); + pr_info("VOUDSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR)); + pr_info("VOUVPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR)); + pr_info("VOUIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR)); + pr_info("VOUSRR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR)); + pr_info("VOUMSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR)); + pr_info("VOUHIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR)); + pr_info("VOUDFR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR)); + pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R)); + pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R)); + pr_info("VOUAIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR)); + pr_info("VOUSWR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR)); + pr_info("VOURCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR)); + pr_info("VOURPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR)); + return 0; +} - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->c = vou_dev->rect; +static int sh_vou_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct sh_vou_device *vou_dev = video_drvdata(file); + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE: + sel->r = vou_dev->rect; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = VOU_MAX_IMAGE_WIDTH; + if (vou_dev->std & V4L2_STD_525_60) + sel->r.height = 480; + else + sel->r.height = 576; + break; + default: + return -EINVAL; + } return 0; } /* Assume a dull encoder, do all the work ourselves. */ -static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) +static int sh_vou_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) { - struct v4l2_crop a_writable = *a; + struct v4l2_rect *rect = &sel->r; struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_rect *rect = &a_writable.c; struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; struct v4l2_pix_format *pix = &vou_dev->pix; struct sh_vou_geometry geo; - struct v4l2_mbus_framefmt mbfmt = { + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, /* Revisit: is this the correct code? */ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .field = V4L2_FIELD_INTERLACED, - .colorspace = V4L2_COLORSPACE_SMPTE170M, + .format.code = MEDIA_BUS_FMT_YUYV8_2X8, + .format.field = V4L2_FIELD_INTERLACED, + .format.colorspace = V4L2_COLORSPACE_SMPTE170M, }; unsigned int img_height_max; int ret; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, - rect->width, rect->height, rect->left, rect->top); - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; + if (vou_dev->std & V4L2_STD_525_60) img_height_max = 480; else img_height_max = 576; - v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &rect->height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&rect->width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 1, + &rect->height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; @@ -984,22 +989,22 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) */ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_crop, &sd_crop); - mbfmt.width = geo.output.width; - mbfmt.height = geo.output.height; - ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_mbus_fmt, &mbfmt); + format.format.width = geo.output.width; + format.format.height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, + set_fmt, NULL, &format); /* Must be implemented, so, don't check for -ENOIOCTLCMD */ if (ret < 0) return ret; /* Sanity checks */ - if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > img_height_max || - mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8) + if ((unsigned)format.format.width > VOU_MAX_IMAGE_WIDTH || + (unsigned)format.format.height > img_height_max || + format.format.code != MEDIA_BUS_FMT_YUYV8_2X8) return -EIO; - geo.output.width = mbfmt.width; - geo.output.height = mbfmt.height; + geo.output.width = format.format.width; + geo.output.height = format.format.height; /* * No down-scaling. According to the API, current call has precedence: @@ -1018,41 +1023,11 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) return 0; } -/* - * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle - * is the initial register values, height takes the interlaced format into - * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can - * actually only meaningfully contain values <= 720 and <= 240 respectively, and - * not <= 864 and <= 312. - */ -static int sh_vou_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *a) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = VOU_MAX_IMAGE_WIDTH; - a->bounds.height = VOU_MAX_IMAGE_HEIGHT; - /* Default = max, set VOUDPR = 0, which is not hardware default */ - a->defrect.left = 0; - a->defrect.top = 0; - a->defrect.width = VOU_MAX_IMAGE_WIDTH; - a->defrect.height = VOU_MAX_IMAGE_HEIGHT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - static irqreturn_t sh_vou_isr(int irq, void *dev_id) { struct sh_vou_device *vou_dev = dev_id; static unsigned long j; - struct videobuf_buffer *vb; + struct sh_vou_buffer *vb; static int cnt; u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked; u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); @@ -1065,7 +1040,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) } spin_lock(&vou_dev->lock); - if (!vou_dev->active || list_empty(&vou_dev->queue)) { + if (!vou_dev->active || list_empty(&vou_dev->buf_list)) { if (printk_timed_ratelimit(&j, 500)) dev_warn(vou_dev->v4l2_dev.dev, "IRQ without active buffer: %x!\n", irq_status); @@ -1087,33 +1062,30 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) sh_vou_reg_a_write(vou_dev, VOUIR, masked); vb = vou_dev->active; - list_del(&vb->queue); - - vb->state = VIDEOBUF_DONE; - v4l2_get_timestamp(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - - if (list_empty(&vou_dev->queue)) { - /* Stop VOU */ - dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n", - __func__, cnt); - sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); - vou_dev->active = NULL; - vou_dev->status = SH_VOU_INITIALISING; - /* Disable End-of-Frame (VSYNC) interrupts */ - sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + if (list_is_singular(&vb->list)) { + /* Keep cycling while no next buffer is available */ + sh_vou_schedule_next(vou_dev, &vb->vb); spin_unlock(&vou_dev->lock); return IRQ_HANDLED; } - vou_dev->active = list_entry(vou_dev->queue.next, - struct videobuf_buffer, queue); + list_del(&vb->list); + + v4l2_get_timestamp(&vb->vb.timestamp); + vb->vb.sequence = vou_dev->sequence++; + vb->vb.field = V4L2_FIELD_INTERLACED; + vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE); + + vou_dev->active = list_entry(vou_dev->buf_list.next, + struct sh_vou_buffer, list); - if (vou_dev->active->queue.next != &vou_dev->queue) { - struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next, - struct videobuf_buffer, queue); - sh_vou_schedule_next(vou_dev, new); + if (list_is_singular(&vou_dev->buf_list)) { + /* Keep cycling while no next buffer is available */ + sh_vou_schedule_next(vou_dev, &vou_dev->active->vb); + } else { + struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next, + struct sh_vou_buffer, list); + sh_vou_schedule_next(vou_dev, &new->vb); } spin_unlock(&vou_dev->lock); @@ -1153,6 +1125,8 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) /* Default - fixed HSYNC length, can be made configurable is required */ sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000); + sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); + return 0; } @@ -1160,96 +1134,47 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) static int sh_vou_open(struct file *file) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), - GFP_KERNEL); - - if (!vou_file) - return -ENOMEM; + int err; - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - if (mutex_lock_interruptible(&vou_dev->fop_lock)) { - kfree(vou_file); + if (mutex_lock_interruptible(&vou_dev->fop_lock)) return -ERESTARTSYS; - } - if (atomic_inc_return(&vou_dev->use_count) == 1) { - int ret; + + err = v4l2_fh_open(file); + if (err) + goto done_open; + if (v4l2_fh_is_singular_file(file) && + vou_dev->status == SH_VOU_INITIALISING) { /* First open */ - vou_dev->status = SH_VOU_INITIALISING; pm_runtime_get_sync(vou_dev->v4l2_dev.dev); - ret = sh_vou_hw_init(vou_dev); - if (ret < 0) { - atomic_dec(&vou_dev->use_count); + err = sh_vou_hw_init(vou_dev); + if (err < 0) { pm_runtime_put(vou_dev->v4l2_dev.dev); + v4l2_fh_release(file); + } else { vou_dev->status = SH_VOU_IDLE; - mutex_unlock(&vou_dev->fop_lock); - kfree(vou_file); - return ret; } } - - videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops, - vou_dev->v4l2_dev.dev, &vou_dev->lock, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_FIELD_NONE, - sizeof(struct videobuf_buffer), - &vou_dev->vdev, &vou_dev->fop_lock); +done_open: mutex_unlock(&vou_dev->fop_lock); - - file->private_data = vou_file; - - return 0; + return err; } static int sh_vou_release(struct file *file) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + bool is_last; - if (!atomic_dec_return(&vou_dev->use_count)) { - mutex_lock(&vou_dev->fop_lock); + mutex_lock(&vou_dev->fop_lock); + is_last = v4l2_fh_is_singular_file(file); + _vb2_fop_release(file, NULL); + if (is_last) { /* Last close */ - vou_dev->status = SH_VOU_IDLE; + vou_dev->status = SH_VOU_INITIALISING; sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); pm_runtime_put(vou_dev->v4l2_dev.dev); - mutex_unlock(&vou_dev->fop_lock); } - - file->private_data = NULL; - kfree(vou_file); - - return 0; -} - -static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - if (mutex_lock_interruptible(&vou_dev->fop_lock)) - return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&vou_file->vbq, vma); - mutex_unlock(&vou_dev->fop_lock); - return ret; -} - -static unsigned int sh_vou_poll(struct file *file, poll_table *wait) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - unsigned int res; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - mutex_lock(&vou_dev->fop_lock); - res = videobuf_poll_stream(file, &vou_file->vbq, wait); mutex_unlock(&vou_dev->fop_lock); - return res; + return 0; } /* sh_vou display ioctl operations */ @@ -1259,17 +1184,23 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out, .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out, .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out, - .vidioc_reqbufs = sh_vou_reqbufs, - .vidioc_querybuf = sh_vou_querybuf, - .vidioc_qbuf = sh_vou_qbuf, - .vidioc_dqbuf = sh_vou_dqbuf, - .vidioc_streamon = sh_vou_streamon, - .vidioc_streamoff = sh_vou_streamoff, + .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_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_g_output = sh_vou_g_output, + .vidioc_s_output = sh_vou_s_output, + .vidioc_enum_output = sh_vou_enum_output, .vidioc_s_std = sh_vou_s_std, .vidioc_g_std = sh_vou_g_std, - .vidioc_cropcap = sh_vou_cropcap, - .vidioc_g_crop = sh_vou_g_crop, - .vidioc_s_crop = sh_vou_s_crop, + .vidioc_g_selection = sh_vou_g_selection, + .vidioc_s_selection = sh_vou_s_selection, + .vidioc_log_status = sh_vou_log_status, }; static const struct v4l2_file_operations sh_vou_fops = { @@ -1277,8 +1208,9 @@ static const struct v4l2_file_operations sh_vou_fops = { .open = sh_vou_open, .release = sh_vou_release, .unlocked_ioctl = video_ioctl2, - .mmap = sh_vou_mmap, - .poll = sh_vou_poll, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, + .write = vb2_fop_write, }; static const struct video_device sh_vou_video_template = { @@ -1297,8 +1229,9 @@ static int sh_vou_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct video_device *vdev; struct sh_vou_device *vou_dev; - struct resource *reg_res, *region; + struct resource *reg_res; struct v4l2_subdev *subdev; + struct vb2_queue *q; int irq, ret; reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1309,16 +1242,16 @@ static int sh_vou_probe(struct platform_device *pdev) return -ENODEV; } - vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL); + vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL); if (!vou_dev) return -ENOMEM; - INIT_LIST_HEAD(&vou_dev->queue); + INIT_LIST_HEAD(&vou_dev->buf_list); spin_lock_init(&vou_dev->lock); mutex_init(&vou_dev->fop_lock); - atomic_set(&vou_dev->use_count, 0); vou_dev->pdata = vou_pdata; - vou_dev->status = SH_VOU_IDLE; + vou_dev->status = SH_VOU_INITIALISING; + vou_dev->pix_idx = 1; rect = &vou_dev->rect; pix = &vou_dev->pix; @@ -1331,34 +1264,24 @@ static int sh_vou_probe(struct platform_device *pdev) rect->height = 480; pix->width = VOU_MAX_IMAGE_WIDTH; pix->height = 480; - pix->pixelformat = V4L2_PIX_FMT_YVYU; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; + pix->pixelformat = V4L2_PIX_FMT_NV16; + pix->field = V4L2_FIELD_INTERLACED; + pix->bytesperline = VOU_MAX_IMAGE_WIDTH; pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - region = request_mem_region(reg_res->start, resource_size(reg_res), - pdev->name); - if (!region) { - dev_err(&pdev->dev, "VOU region already claimed\n"); - ret = -EBUSY; - goto ereqmemreg; - } + vou_dev->base = devm_ioremap_resource(&pdev->dev, reg_res); + if (IS_ERR(vou_dev->base)) + return PTR_ERR(vou_dev->base); - vou_dev->base = ioremap(reg_res->start, resource_size(reg_res)); - if (!vou_dev->base) { - ret = -ENOMEM; - goto emap; - } - - ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev); + ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev); if (ret < 0) - goto ereqirq; + return ret; ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev); if (ret < 0) { dev_err(&pdev->dev, "Error registering v4l2 device\n"); - goto ev4l2devreg; + return ret; } vdev = &vou_dev->vdev; @@ -1371,6 +1294,30 @@ static int sh_vou_probe(struct platform_device *pdev) video_set_drvdata(vdev, vou_dev); + /* Initialize the vb2 queue */ + q = &vou_dev->queue; + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + q->drv_priv = vou_dev; + q->buf_struct_size = sizeof(struct sh_vou_buffer); + q->ops = &sh_vou_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &vou_dev->fop_lock; + ret = vb2_queue_init(q); + if (ret) + goto einitctx; + + vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(vou_dev->alloc_ctx)) { + dev_err(&pdev->dev, "Can't allocate buffer context"); + ret = PTR_ERR(vou_dev->alloc_ctx); + goto einitctx; + } + vdev->queue = q; + INIT_LIST_HEAD(&vou_dev->buf_list); + pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); @@ -1402,41 +1349,27 @@ ei2cnd: ereset: i2c_put_adapter(i2c_adap); ei2cgadap: + vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); +einitctx: pm_runtime_disable(&pdev->dev); v4l2_device_unregister(&vou_dev->v4l2_dev); -ev4l2devreg: - free_irq(irq, vou_dev); -ereqirq: - iounmap(vou_dev->base); -emap: - release_mem_region(reg_res->start, resource_size(reg_res)); -ereqmemreg: - kfree(vou_dev); return ret; } static int sh_vou_remove(struct platform_device *pdev) { - int irq = platform_get_irq(pdev, 0); struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); struct sh_vou_device *vou_dev = container_of(v4l2_dev, struct sh_vou_device, v4l2_dev); struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, struct v4l2_subdev, list); struct i2c_client *client = v4l2_get_subdevdata(sd); - struct resource *reg_res; - if (irq > 0) - free_irq(irq, vou_dev); pm_runtime_disable(&pdev->dev); video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); + vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); v4l2_device_unregister(&vou_dev->v4l2_dev); - iounmap(vou_dev->base); - reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (reg_res) - release_mem_region(reg_res->start, resource_size(reg_res)); - kfree(vou_dev); return 0; } diff --git a/kernel/drivers/media/platform/soc_camera/atmel-isi.c b/kernel/drivers/media/platform/soc_camera/atmel-isi.c index c835beb2a..454f68f0c 100644 --- a/kernel/drivers/media/platform/soc_camera/atmel-isi.c +++ b/kernel/drivers/media/platform/soc_camera/atmel-isi.c @@ -20,21 +20,22 @@ #include #include #include +#include #include -#include #include #include #include #include +#include "atmel-isi.h" + #define MAX_BUFFER_NUM 32 #define MAX_SUPPORT_WIDTH 2048 #define MAX_SUPPORT_HEIGHT 2048 #define VID_LIMIT_BYTES (16 * 1024 * 1024) #define MIN_FRAME_RATE 15 #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) -#define ISI_DEFAULT_MCLK_FREQ 25000000 /* Frame buffer descriptor */ struct fbd { @@ -59,7 +60,7 @@ struct isi_dma_desc { /* Frame buffer data */ struct frame_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct isi_dma_desc *p_dma_desc; struct list_head list; }; @@ -82,8 +83,6 @@ struct atmel_isi { struct completion complete; /* ISI peripherial clock */ struct clk *pclk; - /* ISI_MCK, feed to camera sensor to generate pixel clock */ - struct clk *mck; unsigned int irq; struct isi_platform_data pdata; @@ -104,62 +103,71 @@ static u32 isi_readl(struct atmel_isi *isi, u32 reg) return readl(isi->regs + reg); } -static int configure_geometry(struct atmel_isi *isi, u32 width, +static void configure_geometry(struct atmel_isi *isi, u32 width, u32 height, u32 code) { - u32 cfg2, cr; + u32 cfg2; + /* According to sensor's output format to set cfg2 */ switch (code) { - /* YUV, including grey */ + default: + /* Grey */ case MEDIA_BUS_FMT_Y8_1X8: - cr = ISI_CFG2_GRAYSCALE; + cfg2 = ISI_CFG2_GRAYSCALE | ISI_CFG2_COL_SPACE_YCbCr; break; + /* YUV */ case MEDIA_BUS_FMT_VYUY8_2X8: - cr = ISI_CFG2_YCC_SWAP_MODE_3; + cfg2 = ISI_CFG2_YCC_SWAP_MODE_3 | ISI_CFG2_COL_SPACE_YCbCr; break; case MEDIA_BUS_FMT_UYVY8_2X8: - cr = ISI_CFG2_YCC_SWAP_MODE_2; + cfg2 = ISI_CFG2_YCC_SWAP_MODE_2 | ISI_CFG2_COL_SPACE_YCbCr; break; case MEDIA_BUS_FMT_YVYU8_2X8: - cr = ISI_CFG2_YCC_SWAP_MODE_1; + cfg2 = ISI_CFG2_YCC_SWAP_MODE_1 | ISI_CFG2_COL_SPACE_YCbCr; break; case MEDIA_BUS_FMT_YUYV8_2X8: - cr = ISI_CFG2_YCC_SWAP_DEFAULT; + cfg2 = ISI_CFG2_YCC_SWAP_DEFAULT | ISI_CFG2_COL_SPACE_YCbCr; break; /* RGB, TODO */ - default: - return -EINVAL; } isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); - - cfg2 = isi_readl(isi, ISI_CFG2); - /* Set YCC swap mode */ - cfg2 &= ~ISI_CFG2_YCC_SWAP_MODE_MASK; - cfg2 |= cr; /* Set width */ - cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK); cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & ISI_CFG2_IM_HSIZE_MASK; /* Set height */ - cfg2 &= ~(ISI_CFG2_IM_VSIZE_MASK); cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) & ISI_CFG2_IM_VSIZE_MASK; isi_writel(isi, ISI_CFG2, cfg2); +} - return 0; +static bool is_supported(struct soc_camera_device *icd, + const u32 pixformat) +{ + switch (pixformat) { + /* YUV, including grey */ + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + return true; + /* RGB, TODO */ + default: + return false; + } } static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) { if (isi->active) { - struct vb2_buffer *vb = &isi->active->vb; + struct vb2_v4l2_buffer *vbuf = &isi->active->vb; struct frame_buffer *buf = isi->active; list_del_init(&buf->list); - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.sequence = isi->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vbuf->timestamp); + vbuf->sequence = isi->sequence++; + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); } if (list_empty(&isi->video_buffer_list)) { @@ -227,7 +235,7 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) } timeout = wait_for_completion_timeout(&isi->complete, - msecs_to_jiffies(100)); + msecs_to_jiffies(500)); if (timeout == 0) return -ETIMEDOUT; @@ -237,7 +245,7 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -269,7 +277,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int buffer_init(struct vb2_buffer *vb) { - struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); buf->p_dma_desc = NULL; INIT_LIST_HEAD(&buf->list); @@ -279,8 +288,9 @@ static int buffer_init(struct vb2_buffer *vb) static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); + struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; @@ -294,7 +304,7 @@ static int buffer_prepare(struct vb2_buffer *vb) return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(vb, 0, size); if (!buf->p_dma_desc) { if (list_empty(&isi->dma_desc_head)) { @@ -321,10 +331,11 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_cleanup(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); + struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); /* This descriptor is available now and we add to head list */ if (buf->p_dma_desc) @@ -362,10 +373,11 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); + struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); unsigned long flags = 0; spin_lock_irqsave(&isi->lock, flags); @@ -386,15 +398,21 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct atmel_isi *isi = ici->priv; int ret; + pm_runtime_get_sync(ici->v4l2_dev.dev); + /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); if (ret < 0) { dev_err(icd->parent, "Reset ISI timed out\n"); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } /* Disable all interrupts */ isi_writel(isi, ISI_INTDIS, (u32)~0UL); + configure_geometry(isi, icd->user_width, icd->user_height, + icd->current_fmt->code); + spin_lock_irq(&isi->lock); /* Clear any pending interrupt */ isi_readl(isi, ISI_STATUS); @@ -421,7 +439,7 @@ static void stop_streaming(struct vb2_queue *vq) /* Release all active buffers */ list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { list_del_init(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irq(&isi->lock); @@ -431,11 +449,9 @@ static void stop_streaming(struct vb2_queue *vq) time_before(jiffies, timeout)) msleep(1); - if (time_after(jiffies, timeout)) { + if (time_after(jiffies, timeout)) dev_err(icd->parent, "Timeout waiting for finishing codec request\n"); - return; - } /* Disable interrupts */ isi_writel(isi, ISI_INTDIS, @@ -445,6 +461,8 @@ static void stop_streaming(struct vb2_queue *vq) ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); if (ret < 0) dev_err(icd->parent, "Disable ISI timed out\n"); + + pm_runtime_put(ici->v4l2_dev.dev); } static struct vb2_ops isi_video_qops = { @@ -482,14 +500,19 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, static int isi_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct atmel_isi *isi = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; + /* check with atmel-isi support format, if not support use YUYV */ + if (!is_supported(icd, pix->pixelformat)) + pix->pixelformat = V4L2_PIX_FMT_YUYV; + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { dev_warn(icd->parent, "Format %x not found\n", @@ -500,27 +523,23 @@ static int isi_camera_set_fmt(struct soc_camera_device *icd, dev_dbg(icd->parent, "Plan to set format %dx%d\n", pix->width, pix->height); - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); if (ret < 0) return ret; - if (mf.code != xlate->code) + if (mf->code != xlate->code) return -EINVAL; - ret = configure_geometry(isi, pix->width, pix->height, xlate->code); - if (ret < 0) - return ret; - - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; icd->current_fmt = xlate; dev_dbg(icd->parent, "Finally set format %dx%d\n", @@ -535,10 +554,18 @@ static int isi_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; u32 pixfmt = pix->pixelformat; int ret; + /* check with atmel-isi support format, if not support use YUYV */ + if (!is_supported(icd, pix->pixelformat)) + pix->pixelformat = V4L2_PIX_FMT_YUYV; + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { dev_warn(icd->parent, "Format %x not found\n", pixfmt); @@ -552,21 +579,21 @@ static int isi_camera_try_fmt(struct soc_camera_device *icd, pix->width = MAX_SUPPORT_WIDTH; /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->colorspace = mf->colorspace; - switch (mf.field) { + switch (mf->field) { case V4L2_FIELD_ANY: pix->field = V4L2_FIELD_NONE; break; @@ -574,7 +601,7 @@ static int isi_camera_try_fmt(struct soc_camera_device *icd, break; default: dev_err(icd->parent, "Field type %d unsupported.\n", - mf.field); + mf->field); ret = -EINVAL; } @@ -648,19 +675,22 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int formats = 0, ret; /* sensor format */ - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; /* soc camera host format */ const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { dev_err(icd->parent, - "Invalid format code #%u: %d\n", idx, code); + "Invalid format code #%u: %d\n", idx, code.code); return 0; } @@ -672,7 +702,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, return 0; } - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -680,10 +710,10 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, formats++; if (xlate) { xlate->host_fmt = &isi_camera_formats[0]; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(icd->parent, "Providing format %s using code %d\n", - isi_camera_formats[0].name, code); + isi_camera_formats[0].name, code.code); } break; default: @@ -699,7 +729,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; } @@ -720,37 +750,6 @@ static void isi_camera_remove_device(struct soc_camera_device *icd) icd->devnum); } -/* Called with .host_lock held */ -static int isi_camera_clock_start(struct soc_camera_host *ici) -{ - struct atmel_isi *isi = ici->priv; - int ret; - - ret = clk_prepare_enable(isi->pclk); - if (ret) - return ret; - - if (!IS_ERR(isi->mck)) { - ret = clk_prepare_enable(isi->mck); - if (ret) { - clk_disable_unprepare(isi->pclk); - return ret; - } - } - - return 0; -} - -/* Called with .host_lock held */ -static void isi_camera_clock_stop(struct soc_camera_host *ici) -{ - struct atmel_isi *isi = ici->priv; - - if (!IS_ERR(isi->mck)) - clk_disable_unprepare(isi->mck); - clk_disable_unprepare(isi->pclk); -} - static unsigned int isi_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; @@ -838,6 +837,11 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; + dev_dbg(icd->parent, "vsync active %s, hsync active %s, sampling on pix clock %s edge\n", + common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? "low" : "high", + common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? "low" : "high", + common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING ? "falling" : "rising"); + if (isi->pdata.has_emb_sync) cfg1 |= ISI_CFG1_EMB_SYNC; if (isi->pdata.full_mode) @@ -845,9 +849,14 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) cfg1 |= ISI_CFG1_THMASK_BEATS_16; + /* Enable PM and peripheral clock before operate isi registers */ + pm_runtime_get_sync(ici->v4l2_dev.dev); + isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); isi_writel(isi, ISI_CFG1, cfg1); + pm_runtime_put(ici->v4l2_dev.dev); + return 0; } @@ -855,8 +864,6 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = { .owner = THIS_MODULE, .add = isi_camera_add_device, .remove = isi_camera_remove_device, - .clock_start = isi_camera_clock_start, - .clock_stop = isi_camera_clock_stop, .set_fmt = isi_camera_set_fmt, .try_fmt = isi_camera_try_fmt, .get_formats = isi_camera_get_formats, @@ -879,11 +886,12 @@ static int atmel_isi_remove(struct platform_device *pdev) sizeof(struct fbd) * MAX_BUFFER_NUM, isi->p_fb_descriptors, isi->fb_descriptors_phys); + pm_runtime_disable(&pdev->dev); return 0; } -static int atmel_isi_probe_dt(struct atmel_isi *isi, +static int atmel_isi_parse_dt(struct atmel_isi *isi, struct platform_device *pdev) { struct device_node *np= pdev->dev.of_node; @@ -892,7 +900,6 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi, /* Default settings for ISI */ isi->pdata.full_mode = 1; - isi->pdata.mck_hz = ISI_DEFAULT_MCLK_FREQ; isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; np = of_graph_get_next_endpoint(np, NULL); @@ -902,9 +909,10 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi, } err = v4l2_of_parse_endpoint(np, &ep); + of_node_put(np); if (err) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); - goto err_probe_dt; + return err; } switch (ep.bus.parallel.bus_width) { @@ -918,14 +926,20 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi, default: dev_err(&pdev->dev, "Unsupported bus width: %d\n", ep.bus.parallel.bus_width); - err = -EINVAL; - goto err_probe_dt; + return -EINVAL; } -err_probe_dt: - of_node_put(np); + if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + isi->pdata.hsync_act_low = true; + if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + isi->pdata.vsync_act_low = true; + if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + isi->pdata.pclk_act_falling = true; + + if (ep.bus_type == V4L2_MBUS_BT656) + isi->pdata.has_emb_sync = true; - return err; + return 0; } static int atmel_isi_probe(struct platform_device *pdev) @@ -934,16 +948,7 @@ static int atmel_isi_probe(struct platform_device *pdev) struct atmel_isi *isi; struct resource *regs; int ret, i; - struct device *dev = &pdev->dev; struct soc_camera_host *soc_host; - struct isi_platform_data *pdata; - - pdata = dev->platform_data; - if ((!pdata || !pdata->data_width_flags) && !pdev->dev.of_node) { - dev_err(&pdev->dev, - "No config available for Atmel ISI\n"); - return -EINVAL; - } isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); if (!isi) { @@ -955,34 +960,15 @@ static int atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(isi->pclk)) return PTR_ERR(isi->pclk); - if (pdata) { - memcpy(&isi->pdata, pdata, sizeof(isi->pdata)); - } else { - ret = atmel_isi_probe_dt(isi, pdev); - if (ret) - return ret; - } + ret = atmel_isi_parse_dt(isi, pdev); + if (ret) + return ret; isi->active = NULL; spin_lock_init(&isi->lock); INIT_LIST_HEAD(&isi->video_buffer_list); INIT_LIST_HEAD(&isi->dma_desc_head); - /* ISI_MCK is the sensor master clock. It should be handled by the - * sensor driver directly, as the ISI has no use for that clock. Make - * the clock optional here while platforms transition to the correct - * model. - */ - isi->mck = devm_clk_get(dev, "isi_mck"); - if (!IS_ERR(isi->mck)) { - /* Set ISI_MCK's frequency, it should be faster than pixel - * clock. - */ - ret = clk_set_rate(isi->mck, isi->pdata.mck_hz); - if (ret < 0) - return ret; - } - isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, &isi->fb_descriptors_phys, @@ -1017,8 +1003,6 @@ static int atmel_isi_probe(struct platform_device *pdev) if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) isi->width_flags |= 1 << 9; - isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); - irq = platform_get_irq(pdev, 0); if (IS_ERR_VALUE(irq)) { ret = irq; @@ -1039,10 +1023,8 @@ static int atmel_isi_probe(struct platform_device *pdev) soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; - if (isi->pdata.asd_sizes) { - soc_host->asd = isi->pdata.asd; - soc_host->asd_sizes = isi->pdata.asd_sizes; - } + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); ret = soc_camera_host_register(soc_host); if (ret) { @@ -1052,6 +1034,7 @@ static int atmel_isi_probe(struct platform_device *pdev) return 0; err_register_soc_camera_host: + pm_runtime_disable(&pdev->dev); err_req_irq: err_ioremap: vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); @@ -1064,6 +1047,32 @@ err_alloc_ctx: return ret; } +#ifdef CONFIG_PM +static int atmel_isi_runtime_suspend(struct device *dev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(dev); + struct atmel_isi *isi = container_of(soc_host, + struct atmel_isi, soc_host); + + clk_disable_unprepare(isi->pclk); + + return 0; +} +static int atmel_isi_runtime_resume(struct device *dev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(dev); + struct atmel_isi *isi = container_of(soc_host, + struct atmel_isi, soc_host); + + return clk_prepare_enable(isi->pclk); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops atmel_isi_dev_pm_ops = { + SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, + atmel_isi_runtime_resume, NULL) +}; + static const struct of_device_id atmel_isi_of_match[] = { { .compatible = "atmel,at91sam9g45-isi" }, { } @@ -1075,6 +1084,7 @@ static struct platform_driver atmel_isi_driver = { .driver = { .name = "atmel_isi", .of_match_table = of_match_ptr(atmel_isi_of_match), + .pm = &atmel_isi_dev_pm_ops, }, }; diff --git a/kernel/drivers/media/platform/soc_camera/atmel-isi.h b/kernel/drivers/media/platform/soc_camera/atmel-isi.h new file mode 100644 index 000000000..5acc771d2 --- /dev/null +++ b/kernel/drivers/media/platform/soc_camera/atmel-isi.h @@ -0,0 +1,128 @@ +/* + * Register definitions for the Atmel Image Sensor Interface. + * + * Copyright (C) 2011 Atmel Corporation + * Josh Wu, + * + * Based on previous work by Lars Haring, + * and Sedji Gaouaou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ATMEL_ISI_H__ +#define __ATMEL_ISI_H__ + +#include + +/* ISI_V2 register offsets */ +#define ISI_CFG1 0x0000 +#define ISI_CFG2 0x0004 +#define ISI_PSIZE 0x0008 +#define ISI_PDECF 0x000c +#define ISI_Y2R_SET0 0x0010 +#define ISI_Y2R_SET1 0x0014 +#define ISI_R2Y_SET0 0x0018 +#define ISI_R2Y_SET1 0x001C +#define ISI_R2Y_SET2 0x0020 +#define ISI_CTRL 0x0024 +#define ISI_STATUS 0x0028 +#define ISI_INTEN 0x002C +#define ISI_INTDIS 0x0030 +#define ISI_INTMASK 0x0034 +#define ISI_DMA_CHER 0x0038 +#define ISI_DMA_CHDR 0x003C +#define ISI_DMA_CHSR 0x0040 +#define ISI_DMA_P_ADDR 0x0044 +#define ISI_DMA_P_CTRL 0x0048 +#define ISI_DMA_P_DSCR 0x004C +#define ISI_DMA_C_ADDR 0x0050 +#define ISI_DMA_C_CTRL 0x0054 +#define ISI_DMA_C_DSCR 0x0058 + +/* Bitfields in CFG1 */ +#define ISI_CFG1_HSYNC_POL_ACTIVE_LOW (1 << 2) +#define ISI_CFG1_VSYNC_POL_ACTIVE_LOW (1 << 3) +#define ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING (1 << 4) +#define ISI_CFG1_EMB_SYNC (1 << 6) +#define ISI_CFG1_CRC_SYNC (1 << 7) +/* Constants for FRATE(ISI_V2) */ +#define ISI_CFG1_FRATE_CAPTURE_ALL (0 << 8) +#define ISI_CFG1_FRATE_DIV_2 (1 << 8) +#define ISI_CFG1_FRATE_DIV_3 (2 << 8) +#define ISI_CFG1_FRATE_DIV_4 (3 << 8) +#define ISI_CFG1_FRATE_DIV_5 (4 << 8) +#define ISI_CFG1_FRATE_DIV_6 (5 << 8) +#define ISI_CFG1_FRATE_DIV_7 (6 << 8) +#define ISI_CFG1_FRATE_DIV_8 (7 << 8) +#define ISI_CFG1_FRATE_DIV_MASK (7 << 8) +#define ISI_CFG1_DISCR (1 << 11) +#define ISI_CFG1_FULL_MODE (1 << 12) +/* Definition for THMASK(ISI_V2) */ +#define ISI_CFG1_THMASK_BEATS_4 (0 << 13) +#define ISI_CFG1_THMASK_BEATS_8 (1 << 13) +#define ISI_CFG1_THMASK_BEATS_16 (2 << 13) + +/* Bitfields in CFG2 */ +#define ISI_CFG2_GRAYSCALE (1 << 13) +#define ISI_CFG2_COL_SPACE_YCbCr (0 << 15) +#define ISI_CFG2_COL_SPACE_RGB (1 << 15) +/* Constants for YCC_SWAP(ISI_V2) */ +#define ISI_CFG2_YCC_SWAP_DEFAULT (0 << 28) +#define ISI_CFG2_YCC_SWAP_MODE_1 (1 << 28) +#define ISI_CFG2_YCC_SWAP_MODE_2 (2 << 28) +#define ISI_CFG2_YCC_SWAP_MODE_3 (3 << 28) +#define ISI_CFG2_YCC_SWAP_MODE_MASK (3 << 28) +#define ISI_CFG2_IM_VSIZE_OFFSET 0 +#define ISI_CFG2_IM_HSIZE_OFFSET 16 +#define ISI_CFG2_IM_VSIZE_MASK (0x7FF << ISI_CFG2_IM_VSIZE_OFFSET) +#define ISI_CFG2_IM_HSIZE_MASK (0x7FF << ISI_CFG2_IM_HSIZE_OFFSET) + +/* Bitfields in CTRL */ +/* Also using in SR(ISI_V2) */ +#define ISI_CTRL_EN (1 << 0) +#define ISI_CTRL_CDC (1 << 8) +/* Also using in SR/IER/IDR/IMR(ISI_V2) */ +#define ISI_CTRL_DIS (1 << 1) +#define ISI_CTRL_SRST (1 << 2) + +/* Bitfields in SR */ +#define ISI_SR_SIP (1 << 19) +/* Also using in SR/IER/IDR/IMR */ +#define ISI_SR_VSYNC (1 << 10) +#define ISI_SR_PXFR_DONE (1 << 16) +#define ISI_SR_CXFR_DONE (1 << 17) +#define ISI_SR_P_OVR (1 << 24) +#define ISI_SR_C_OVR (1 << 25) +#define ISI_SR_CRC_ERR (1 << 26) +#define ISI_SR_FR_OVR (1 << 27) + +/* Bitfields in DMA_C_CTRL & in DMA_P_CTRL */ +#define ISI_DMA_CTRL_FETCH (1 << 0) +#define ISI_DMA_CTRL_WB (1 << 1) +#define ISI_DMA_CTRL_IEN (1 << 2) +#define ISI_DMA_CTRL_DONE (1 << 3) + +/* Bitfields in DMA_CHSR/CHER/CHDR */ +#define ISI_DMA_CHSR_P_CH (1 << 0) +#define ISI_DMA_CHSR_C_CH (1 << 1) + +/* Definition for isi_platform_data */ +#define ISI_DATAWIDTH_8 0x01 +#define ISI_DATAWIDTH_10 0x02 + +struct v4l2_async_subdev; + +struct isi_platform_data { + u8 has_emb_sync; + u8 hsync_act_low; + u8 vsync_act_low; + u8 pclk_act_falling; + u8 full_mode; + u32 data_width_flags; + /* Using for ISI_CFG1 */ + u32 frate; +}; + +#endif /* __ATMEL_ISI_H__ */ diff --git a/kernel/drivers/media/platform/soc_camera/mx2_camera.c b/kernel/drivers/media/platform/soc_camera/mx2_camera.c index 192377f55..1f28d21a3 100644 --- a/kernel/drivers/media/platform/soc_camera/mx2_camera.c +++ b/kernel/drivers/media/platform/soc_camera/mx2_camera.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include #include @@ -225,7 +225,7 @@ struct mx2_buf_internal { /* buffer for one video frame */ struct mx2_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct mx2_buf_internal internal; }; @@ -469,10 +469,11 @@ static void mx2_camera_clock_stop(struct soc_camera_host *ici) * Videobuf operations */ static int mx2_videobuf_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; @@ -530,11 +531,12 @@ out: static void mx2_videobuf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); + struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb); unsigned long flags; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, @@ -664,7 +666,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) buf = list_first_entry(&pcdev->capture, struct mx2_buffer, internal.queue); buf->internal.bufnum = 0; - vb = &buf->vb; + vb = &buf->vb.vb2_buf; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -673,7 +675,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) buf = list_first_entry(&pcdev->capture, struct mx2_buffer, internal.queue); buf->internal.bufnum = 1; - vb = &buf->vb; + vb = &buf->vb.vb2_buf; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -912,7 +914,10 @@ static int mx2_camera_set_crop(struct soc_camera_device *icd, struct v4l2_crop a_writable = *a; struct v4l2_rect *rect = &a_writable.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); @@ -923,15 +928,15 @@ static int mx2_camera_set_crop(struct soc_camera_device *icd, return ret; /* The capture device might have changed its output */ - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf.width, mf.height); + mf->width, mf->height); - icd->user_width = mf.width; - icd->user_height = mf.height; + icd->user_width = mf->width; + icd->user_height = mf->height; return ret; } @@ -943,22 +948,25 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_mbus_pixelfmt *fmt; struct device *dev = icd->parent; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; int ret, formats = 0; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* no more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code); + dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); return 0; } - if (code == MEDIA_BUS_FMT_YUYV8_2X8 || - code == MEDIA_BUS_FMT_UYVY8_2X8) { + if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 || + code.code == MEDIA_BUS_FMT_UYVY8_2X8) { formats++; if (xlate) { /* @@ -967,21 +975,21 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, */ xlate->host_fmt = soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8); - xlate->code = code; + xlate->code = code.code; dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code); + xlate->host_fmt->name, code.code); xlate++; } } - if (code == MEDIA_BUS_FMT_UYVY8_2X8) { + if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) { formats++; if (xlate) { xlate->host_fmt = soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8); - xlate->code = code; + xlate->code = code.code; dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code); + xlate->host_fmt->name, code.code); xlate++; } } @@ -990,7 +998,7 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; } return formats; @@ -1121,7 +1129,10 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", @@ -1134,19 +1145,19 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, return -EINVAL; } - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; /* Store width and height returned by the sensor for resizing */ - pcdev->s_width = mf.width; - pcdev->s_height = mf.height; + pcdev->s_width = mf->width; + pcdev->s_height = mf->height; dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", __func__, pcdev->s_width, pcdev->s_height); @@ -1154,19 +1165,19 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, xlate->host_fmt->fourcc); memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); - if ((mf.width != pix->width || mf.height != pix->height) && + if ((mf->width != pix->width || mf->height != pix->height) && pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) + if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0) dev_dbg(icd->parent, "%s: can't resize\n", __func__); } - if (mf.code != xlate->code) + if (mf->code != xlate->code) return -EINVAL; - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; icd->current_fmt = xlate; dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", @@ -1181,7 +1192,11 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; __u32 pixfmt = pix->pixelformat; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; @@ -1204,13 +1219,13 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->width &= ~0x7; /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1221,29 +1236,29 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, emma_prp = mx27_emma_prp_get_format(xlate->code, xlate->host_fmt->fourcc); - if ((mf.width != pix->width || mf.height != pix->height) && + if ((mf->width != pix->width || mf->height != pix->height) && emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) + if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0) dev_dbg(icd->parent, "%s: can't resize\n", __func__); } - if (mf.field == V4L2_FIELD_ANY) - mf.field = V4L2_FIELD_NONE; + if (mf->field == V4L2_FIELD_ANY) + mf->field = V4L2_FIELD_NONE; /* * Driver supports interlaced images provided they have * both fields so that they can be processed as if they * were progressive. */ - if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) { + if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) { dev_err(icd->parent, "Field type %d unsupported.\n", - mf.field); + mf->field); return -EINVAL; } - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", __func__, pix->width, pix->height); @@ -1294,6 +1309,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, struct mx2_buf_internal *ibuf; struct mx2_buffer *buf; struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; unsigned long phys; ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, @@ -1310,7 +1326,8 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, } else { buf = mx2_ibuf_to_buf(ibuf); - vb = &buf->vb; + vb = &buf->vb.vb2_buf; + vbuf = to_vb2_v4l2_buffer(vb); #ifdef DEBUG phys = vb2_dma_contig_plane_dma_addr(vb, 0); if (prp->cfg.channel == 1) { @@ -1334,8 +1351,8 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, vb2_get_plane_payload(vb, 0)); list_del_init(&buf->internal.queue); - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.sequence = pcdev->frame_count; + v4l2_get_timestamp(&vbuf->timestamp); + vbuf->sequence = pcdev->frame_count; if (err) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); else @@ -1367,7 +1384,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - vb = &buf->vb; + vb = &buf->vb.vb2_buf; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, bufnum); diff --git a/kernel/drivers/media/platform/soc_camera/mx3_camera.c b/kernel/drivers/media/platform/soc_camera/mx3_camera.c index 3435fd2ca..49c3a257a 100644 --- a/kernel/drivers/media/platform/soc_camera/mx3_camera.c +++ b/kernel/drivers/media/platform/soc_camera/mx3_camera.c @@ -63,7 +63,7 @@ struct mx3_camera_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head queue; /* One descriptot per scatterlist (per frame) */ @@ -133,7 +133,7 @@ static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) __raw_writel(value, mx3->base + reg); } -static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb) +static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb) { return container_of(vb, struct mx3_camera_buffer, vb); } @@ -151,14 +151,14 @@ static void mx3_cam_dma_done(void *arg) spin_lock(&mx3_cam->lock); if (mx3_cam->active) { - struct vb2_buffer *vb = &mx3_cam->active->vb; + struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb; struct mx3_camera_buffer *buf = to_mx3_vb(vb); list_del_init(&buf->queue); - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.field = mx3_cam->field; - vb->v4l2_buf.sequence = mx3_cam->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&vb->timestamp); + vb->field = mx3_cam->field; + vb->sequence = mx3_cam->sequence++; + vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); } if (list_empty(&mx3_cam->capture)) { @@ -185,10 +185,11 @@ static void mx3_cam_dma_done(void *arg) * Calculate the __buffer__ (not data) size and number of buffers. */ static int mx3_videobuf_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; @@ -257,10 +258,11 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) static void mx3_videobuf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); + struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); struct scatterlist *sg = &buf->sg; struct dma_async_tx_descriptor *txd; struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; @@ -273,7 +275,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) if (vb2_plane_size(vb, 0) < new_size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", - vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size); + vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size); goto error; } @@ -357,10 +359,11 @@ error: static void mx3_videobuf_release(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); + struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); struct dma_async_tx_descriptor *txd = buf->txd; unsigned long flags; @@ -390,10 +393,11 @@ static void mx3_videobuf_release(struct vb2_buffer *vb) static int mx3_videobuf_init(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); + struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); if (!buf->txd) { /* This is for locking debugging only */ @@ -424,7 +428,7 @@ static void mx3_stop_streaming(struct vb2_queue *q) list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { list_del_init(&buf->queue); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&mx3_cam->lock, flags); @@ -659,18 +663,21 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; int formats = 0, ret; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { dev_warn(icd->parent, - "Unsupported format code #%u: 0x%x\n", idx, code); + "Unsupported format code #%u: 0x%x\n", idx, code.code); return 0; } @@ -679,25 +686,25 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id if (ret < 0) return 0; - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_SBGGR10_1X10: formats++; if (xlate) { xlate->host_fmt = &mx3_camera_formats[0]; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[0].name, code); + mx3_camera_formats[0].name, code.code); } break; case MEDIA_BUS_FMT_Y10_1X10: formats++; if (xlate) { xlate->host_fmt = &mx3_camera_formats[1]; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[1].name, code); + mx3_camera_formats[1].name, code.code); } break; default: @@ -709,7 +716,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n", (fmt->fourcc >> (0*8)) & 0xFF, (fmt->fourcc >> (1*8)) & 0xFF, @@ -801,7 +808,10 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); @@ -812,30 +822,30 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, return ret; /* The capture device might have changed its output sizes */ - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; - if (mf.code != icd->current_fmt->code) + if (mf->code != icd->current_fmt->code) return -EINVAL; - if (mf.width & 7) { + if (mf->width & 7) { /* Ouch! We can only handle 8-byte aligned width... */ - stride_align(&mf.width); - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + stride_align(&mf->width); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); if (ret < 0) return ret; } - if (mf.width != icd->user_width || mf.height != icd->user_height) - configure_geometry(mx3_cam, mf.width, mf.height, + if (mf->width != icd->user_width || mf->height != icd->user_height) + configure_geometry(mx3_cam, mf->width, mf->height, icd->current_fmt->host_fmt); dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf.width, mf.height); + mf->width, mf->height); - icd->user_width = mf.width; - icd->user_height = mf.height; + icd->user_width = mf->width; + icd->user_height = mf->height; return ret; } @@ -848,7 +858,10 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); @@ -869,17 +882,17 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt); - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); if (ret < 0) return ret; - if (mf.code != xlate->code) + if (mf->code != xlate->code) return -EINVAL; if (!mx3_cam->idmac_channel[0]) { @@ -888,11 +901,11 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, return ret; } - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - mx3_cam->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + mx3_cam->field = mf->field; + pix->colorspace = mf->colorspace; icd->current_fmt = xlate; dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height); @@ -906,7 +919,11 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; __u32 pixfmt = pix->pixelformat; int ret; @@ -923,21 +940,21 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, pix->width = 4096; /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->colorspace = mf->colorspace; - switch (mf.field) { + switch (mf->field) { case V4L2_FIELD_ANY: pix->field = V4L2_FIELD_NONE; break; @@ -945,7 +962,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, break; default: dev_err(icd->parent, "Field type %d unsupported.\n", - mf.field); + mf->field); ret = -EINVAL; } diff --git a/kernel/drivers/media/platform/soc_camera/omap1_camera.c b/kernel/drivers/media/platform/soc_camera/omap1_camera.c index 16f65ecb7..ba8dcd11a 100644 --- a/kernel/drivers/media/platform/soc_camera/omap1_camera.c +++ b/kernel/drivers/media/platform/soc_camera/omap1_camera.c @@ -1068,18 +1068,21 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; int formats = 0, ret; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__, - idx, code); + idx, code.code); return 0; } @@ -1087,7 +1090,7 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, if (fmt->bits_per_sample != 8) return 0; - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: @@ -1098,14 +1101,14 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, case MEDIA_BUS_FMT_RGB565_2X8_LE: formats++; if (xlate) { - xlate->host_fmt = soc_mbus_find_fmtdesc(code, + xlate->host_fmt = soc_mbus_find_fmtdesc(code.code, omap1_cam_formats, ARRAY_SIZE(omap1_cam_formats)); - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "%s: providing format %s as byte swapped code #%d\n", - __func__, xlate->host_fmt->name, code); + __func__, xlate->host_fmt->name, code.code); } default: if (xlate) @@ -1116,7 +1119,7 @@ static int omap1_cam_get_formats(struct soc_camera_device *icd, formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; } @@ -1154,7 +1157,7 @@ static int dma_align(int *width, int *height, return 1; } -#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \ +#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \ ({ \ struct soc_camera_sense sense = { \ .master_clock = pcdev->camexclk, \ @@ -1165,7 +1168,7 @@ static int dma_align(int *width, int *height, if (pcdev->pdata) \ sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ icd->sense = &sense; \ - __ret = v4l2_subdev_call(sd, video, function, ##args); \ + __ret = v4l2_subdev_call(sd, op, function, ##args); \ icd->sense = NULL; \ \ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ @@ -1179,16 +1182,17 @@ static int dma_align(int *width, int *height, __ret; \ }) -static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev, +static int set_format(struct omap1_cam_dev *pcdev, struct device *dev, struct soc_camera_device *icd, struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf, + struct v4l2_subdev_format *format, const struct soc_camera_format_xlate *xlate) { s32 bytes_per_line; - int ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, mf); + struct v4l2_mbus_framefmt *mf = &format->format; + int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format); if (ret < 0) { - dev_err(dev, "%s: s_mbus_fmt failed\n", __func__); + dev_err(dev, "%s: set_fmt failed\n", __func__); return ret; } @@ -1221,42 +1225,45 @@ static int omap1_cam_set_crop(struct soc_camera_device *icd, struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; - ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop); + ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop); if (ret < 0) { dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__, rect->width, rect->height, rect->left, rect->top); return ret; } - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) { dev_warn(dev, "%s: failed to fetch current format\n", __func__); return ret; } - ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode, + ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, false); if (ret < 0) { dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, mf.width, mf.height, + __func__, mf->width, mf->height, xlate->host_fmt->name); return ret; } if (!ret) { /* sensor returned geometry not DMA aligned, trying to fix */ - ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate); + ret = set_format(pcdev, dev, icd, sd, &fmt, xlate); if (ret < 0) { dev_err(dev, "%s: failed to set format\n", __func__); return ret; } } - icd->user_width = mf.width; - icd->user_height = mf.height; + icd->user_width = mf->width; + icd->user_height = mf->height; return 0; } @@ -1270,7 +1277,10 @@ static int omap1_cam_set_fmt(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); @@ -1280,13 +1290,13 @@ static int omap1_cam_set_fmt(struct soc_camera_device *icd, return -EINVAL; } - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode, + ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, true); if (ret < 0) { dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", @@ -1295,16 +1305,16 @@ static int omap1_cam_set_fmt(struct soc_camera_device *icd, return ret; } - ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate); + ret = set_format(pcdev, dev, icd, sd, &format, xlate); if (ret < 0) { dev_err(dev, "%s: failed to set format\n", __func__); return ret; } - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; icd->current_fmt = xlate; return 0; @@ -1316,7 +1326,11 @@ static int omap1_cam_try_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; /* TODO: limit to mx1 hardware capabilities */ @@ -1327,21 +1341,21 @@ static int omap1_cam_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; /* limit to sensor capabilities */ - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; return 0; } diff --git a/kernel/drivers/media/platform/soc_camera/pxa_camera.c b/kernel/drivers/media/platform/soc_camera/pxa_camera.c index 8d6e343fe..fcb942de0 100644 --- a/kernel/drivers/media/platform/soc_camera/pxa_camera.c +++ b/kernel/drivers/media/platform/soc_camera/pxa_camera.c @@ -1253,17 +1253,20 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id struct device *dev = icd->parent; int formats = 0, ret; struct pxa_cam *cam; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code); + dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); return 0; } @@ -1282,15 +1285,15 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id cam = icd->host_priv; } - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: formats++; if (xlate) { xlate->host_fmt = &pxa_camera_formats[0]; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "Providing format %s using code %d\n", - pxa_camera_formats[0].name, code); + pxa_camera_formats[0].name, code.code); } case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -1314,7 +1317,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; } @@ -1346,7 +1349,10 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; struct pxa_cam *cam = icd->host_priv; u32 fourcc = icd->current_fmt->host_fmt->fourcc; int ret; @@ -1365,23 +1371,23 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, return ret; } - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; - if (pxa_camera_check_frame(mf.width, mf.height)) { + if (pxa_camera_check_frame(mf->width, mf->height)) { /* * Camera cropping produced a frame beyond our capabilities. * FIXME: just extract a subframe, that we can process. */ - v4l_bound_align_image(&mf.width, 48, 2048, 1, - &mf.height, 32, 2048, 0, + v4l_bound_align_image(&mf->width, 48, 2048, 1, + &mf->height, 32, 2048, 0, fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); if (ret < 0) return ret; - if (pxa_camera_check_frame(mf.width, mf.height)) { + if (pxa_camera_check_frame(mf->width, mf->height)) { dev_warn(icd->parent, "Inconsistent state. Use S_FMT to repair\n"); return -EINVAL; @@ -1398,8 +1404,8 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, recalculate_fifo_timeout(pcdev, sense.pixel_clock); } - icd->user_width = mf.width; - icd->user_height = mf.height; + icd->user_width = mf->width; + icd->user_height = mf->height; pxa_camera_setup_cicr(icd, cam->flags, fourcc); @@ -1419,7 +1425,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, .pixel_clock_max = pcdev->ciclk / 4, }; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); @@ -1433,15 +1442,15 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, /* The caller holds a mutex. */ icd->sense = &sense; - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (mf.code != xlate->code) + if (mf->code != xlate->code) return -EINVAL; icd->sense = NULL; @@ -1449,10 +1458,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0) { dev_warn(dev, "Failed to configure for format %x\n", pix->pixelformat); - } else if (pxa_camera_check_frame(mf.width, mf.height)) { + } else if (pxa_camera_check_frame(mf->width, mf->height)) { dev_warn(dev, "Camera driver produced an unsupported frame %dx%d\n", - mf.width, mf.height); + mf->width, mf->height); ret = -EINVAL; } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { @@ -1467,10 +1476,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; icd->current_fmt = xlate; return ret; @@ -1482,7 +1491,11 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; __u32 pixfmt = pix->pixelformat; int ret; @@ -1503,22 +1516,22 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; + mf->width = pix->width; + mf->height = pix->height; /* Only progressive video supported so far */ - mf.field = V4L2_FIELD_NONE; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = pix->colorspace; + mf->code = xlate->code; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->colorspace = mf->colorspace; - switch (mf.field) { + switch (mf->field) { case V4L2_FIELD_ANY: case V4L2_FIELD_NONE: pix->field = V4L2_FIELD_NONE; @@ -1526,7 +1539,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, default: /* TODO: support interlaced at least in pass-through mode */ dev_err(icd->parent, "Field type %d unsupported.\n", - mf.field); + mf->field); return -EINVAL; } diff --git a/kernel/drivers/media/platform/soc_camera/rcar_vin.c b/kernel/drivers/media/platform/soc_camera/rcar_vin.c index 6460f8e1b..efe57b23f 100644 --- a/kernel/drivers/media/platform/soc_camera/rcar_vin.c +++ b/kernel/drivers/media/platform/soc_camera/rcar_vin.c @@ -98,6 +98,7 @@ #define VNMC_INF_YUV10_BT656 (2 << 16) #define VNMC_INF_YUV10_BT601 (3 << 16) #define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) #define VNMC_VUP (1 << 10) #define VNMC_IM_ODD (0 << 3) #define VNMC_IM_ODD_EVEN (1 << 3) @@ -477,7 +478,7 @@ struct rcar_vin_priv { struct soc_camera_host ici; struct list_head capture; #define MAX_BUFFER_NUM 3 - struct vb2_buffer *queue_buf[MAX_BUFFER_NUM]; + struct vb2_v4l2_buffer *queue_buf[MAX_BUFFER_NUM]; struct vb2_alloc_ctx *alloc_ctx; enum v4l2_field field; unsigned int pdata_flags; @@ -491,7 +492,7 @@ struct rcar_vin_priv { #define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM) struct rcar_vin_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -526,11 +527,12 @@ struct rcar_vin_cam { * required */ static int rcar_vin_videobuf_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct rcar_vin_priv *priv = ici->priv; @@ -540,6 +542,9 @@ static int rcar_vin_videobuf_setup(struct vb2_queue *vq, unsigned int bytes_per_line; int ret; + if (fmt->fmt.pix.sizeimage < icd->sizeimage) + return -EINVAL; + xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); if (!xlate) @@ -589,7 +594,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) struct soc_camera_device *icd = priv->ici.icd; struct rcar_vin_cam *cam = icd->host_priv; u32 vnmc, dmr, interrupts; - bool progressive = false, output_is_yuv = false; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; switch (priv->field) { case V4L2_FIELD_TOP: @@ -623,16 +628,22 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) case MEDIA_BUS_FMT_YUYV8_1X16: /* BT.601/BT.1358 16bit YCbCr422 */ vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; break; case MEDIA_BUS_FMT_YUYV8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; break; case MEDIA_BUS_FMT_YUYV10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; break; default: break; @@ -676,7 +687,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) vnmc |= VNMC_VUP; /* If input and output use the same colorspace, use bypass mode */ - if (output_is_yuv) + if (input_is_yuv == output_is_yuv) vnmc |= VNMC_BPS; /* progressive or interlaced mode */ @@ -738,7 +749,7 @@ static int rcar_vin_hw_ready(struct rcar_vin_priv *priv) /* Moves a buffer from the queue to the HW slots */ static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) { - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; dma_addr_t phys_addr_top; int slot; @@ -750,10 +761,11 @@ static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) if (slot < 0) return 0; - vb = &list_entry(priv->capture.next, struct rcar_vin_buffer, list)->vb; - list_del_init(to_buf_list(vb)); - priv->queue_buf[slot] = vb; - phys_addr_top = vb2_dma_contig_plane_dma_addr(vb, 0); + vbuf = &list_entry(priv->capture.next, + struct rcar_vin_buffer, list)->vb; + list_del_init(to_buf_list(vbuf)); + priv->queue_buf[slot] = vbuf; + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); return 1; @@ -761,6 +773,7 @@ static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct rcar_vin_priv *priv = ici->priv; @@ -770,7 +783,7 @@ static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", - vb->v4l2_buf.index, vb2_plane_size(vb, 0), size); + vb->index, vb2_plane_size(vb, 0), size); goto error; } @@ -781,14 +794,14 @@ static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) spin_lock_irq(&priv->lock); - list_add_tail(to_buf_list(vb), &priv->capture); + list_add_tail(to_buf_list(vbuf), &priv->capture); rcar_vin_fill_hw_slot(priv); /* If we weren't running, and have enough buffers, start capturing! */ if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) { if (rcar_vin_setup(priv)) { /* Submit error */ - list_del_init(to_buf_list(vb)); + list_del_init(to_buf_list(vbuf)); spin_unlock_irq(&priv->lock); goto error; } @@ -844,7 +857,7 @@ static void rcar_vin_stop_streaming(struct vb2_queue *vq) for (i = 0; i < MAX_BUFFER_NUM; i++) { if (priv->queue_buf[i]) { - vb2_buffer_done(priv->queue_buf[i], + vb2_buffer_done(&priv->queue_buf[i]->vb2_buf, VB2_BUF_STATE_ERROR); priv->queue_buf[i] = NULL; } @@ -852,7 +865,7 @@ static void rcar_vin_stop_streaming(struct vb2_queue *vq) list_for_each_safe(buf_head, tmp, &priv->capture) { vb2_buffer_done(&list_entry(buf_head, - struct rcar_vin_buffer, list)->vb, + struct rcar_vin_buffer, list)->vb.vb2_buf, VB2_BUF_STATE_ERROR); list_del_init(buf_head); } @@ -897,10 +910,11 @@ static irqreturn_t rcar_vin_irq(int irq, void *data) else slot = 0; - priv->queue_buf[slot]->v4l2_buf.field = priv->field; - priv->queue_buf[slot]->v4l2_buf.sequence = priv->sequence++; - do_gettimeofday(&priv->queue_buf[slot]->v4l2_buf.timestamp); - vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE); + priv->queue_buf[slot]->field = priv->field; + priv->queue_buf[slot]->sequence = priv->sequence++; + v4l2_get_timestamp(&priv->queue_buf[slot]->timestamp); + vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf, + VB2_BUF_STATE_DONE); priv->queue_buf[slot] = NULL; if (priv->state != STOPPING) @@ -954,7 +968,7 @@ static void rcar_vin_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct rcar_vin_priv *priv = ici->priv; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; int i; /* disable capture, disable interrupts */ @@ -968,10 +982,10 @@ static void rcar_vin_remove_device(struct soc_camera_device *icd) /* make sure active buffer is cancelled */ spin_lock_irq(&priv->lock); for (i = 0; i < MAX_BUFFER_NUM; i++) { - vb = priv->queue_buf[i]; - if (vb) { - list_del_init(to_buf_list(vb)); - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + vbuf = priv->queue_buf[i]; + if (vbuf) { + list_del_init(to_buf_list(vbuf)); + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR); } } spin_unlock_irq(&priv->lock); @@ -1323,16 +1337,19 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, int ret, k, n; int formats = 0; struct rcar_vin_cam *cam; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); return 0; } @@ -1341,12 +1358,15 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, return 0; if (!icd->host_priv) { - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; struct v4l2_rect rect; struct device *dev = icd->parent; int shift; - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; @@ -1356,8 +1376,8 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, /* Sensor driver doesn't support cropping */ rect.left = 0; rect.top = 0; - rect.width = mf.width; - rect.height = mf.height; + rect.width = mf->width; + rect.height = mf->height; } else if (ret < 0) { return ret; } @@ -1367,16 +1387,16 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, * 1280x960, 640x480, 320x240 */ for (shift = 0; shift < 3; shift++) { - if (mf.width <= VIN_MAX_WIDTH && - mf.height <= VIN_MAX_HEIGHT) + if (mf->width <= VIN_MAX_WIDTH && + mf->height <= VIN_MAX_HEIGHT) break; - mf.width = 1280 >> shift; - mf.height = 960 >> shift; + mf->width = 1280 >> shift; + mf->height = 960 >> shift; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - video, s_mbus_fmt, - &mf); + pad, set_fmt, NULL, + &fmt); if (ret < 0) return ret; } @@ -1384,11 +1404,11 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, if (shift == 3) { dev_err(dev, "Failed to configure the client below %ux%u\n", - mf.width, mf.height); + mf->width, mf->height); return -EIO; } - dev_dbg(dev, "camera fmt %ux%u\n", mf.width, mf.height); + dev_dbg(dev, "camera fmt %ux%u\n", mf->width, mf->height); cam = kzalloc(sizeof(*cam), GFP_KERNEL); if (!cam) @@ -1399,10 +1419,10 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, */ cam->rect = rect; cam->subrect = rect; - cam->width = mf.width; - cam->height = mf.height; - cam->out_width = mf.width; - cam->out_height = mf.height; + cam->width = mf->width; + cam->height = mf->height; + cam->out_width = mf->width; + cam->out_height = mf->height; icd->host_priv = cam; } else { @@ -1413,10 +1433,11 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, if (!idx) cam->extra_fmt = NULL; - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: if (cam->extra_fmt) break; @@ -1427,9 +1448,9 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, formats += n; for (k = 0; xlate && k < n; k++, xlate++) { xlate->host_fmt = &rcar_vin_formats[k]; - xlate->code = code; + xlate->code = code.code; dev_dbg(dev, "Providing format %s using code %d\n", - rcar_vin_formats[k].name, code); + rcar_vin_formats[k].name, code.code); } break; default: @@ -1445,7 +1466,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; } @@ -1470,7 +1491,10 @@ static int rcar_vin_set_crop(struct soc_camera_device *icd, struct v4l2_rect *cam_rect = &cam_crop.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; u32 vnmc; int ret, i; @@ -1494,16 +1518,16 @@ static int rcar_vin_set_crop(struct soc_camera_device *icd, /* On success cam_crop contains current camera crop */ /* Retrieve camera output window */ - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; - if (mf.width > VIN_MAX_WIDTH || mf.height > VIN_MAX_HEIGHT) + if (mf->width > VIN_MAX_WIDTH || mf->height > VIN_MAX_HEIGHT) return -EINVAL; /* Cache camera output window */ - cam->width = mf.width; - cam->height = mf.height; + cam->width = mf->width; + cam->height = mf->height; icd->user_width = cam->width; icd->user_height = cam->height; @@ -1582,11 +1606,15 @@ static int rcar_vin_set_fmt(struct soc_camera_device *icd, case V4L2_FIELD_INTERLACED: /* Query for standard if not explicitly mentioned _TB/_BT */ ret = v4l2_subdev_call(sd, video, querystd, &std); - if (ret < 0) - std = V4L2_STD_625_50; - - field = std & V4L2_STD_625_50 ? V4L2_FIELD_INTERLACED_TB : - V4L2_FIELD_INTERLACED_BT; + if (ret == -ENOIOCTLCMD) { + field = V4L2_FIELD_NONE; + } else if (ret < 0) { + return ret; + } else { + field = std & V4L2_STD_625_50 ? + V4L2_FIELD_INTERLACED_TB : + V4L2_FIELD_INTERLACED_BT; + } break; } @@ -1679,7 +1707,11 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd, const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; __u32 pixfmt = pix->pixelformat; int width, height; int ret; @@ -1706,25 +1738,25 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd, pix->sizeimage = 0; /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.code = xlate->code; - mf.colorspace = pix->colorspace; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->code = xlate->code; + mf->colorspace = pix->colorspace; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - video, try_mbus_fmt, &mf); + pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; /* Adjust only if VIN cannot scale */ - if (pix->width > mf.width * 2) - pix->width = mf.width * 2; - if (pix->height > mf.height * 3) - pix->height = mf.height * 3; + if (pix->width > mf->width * 2) + pix->width = mf->width * 2; + if (pix->height > mf->height * 3) + pix->height = mf->height * 3; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->field = mf->field; + pix->colorspace = mf->colorspace; if (pixfmt == V4L2_PIX_FMT_NV16) { /* FIXME: check against rect_max after converting soc-camera */ @@ -1735,12 +1767,12 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd, * requested a bigger rectangle, it will not return a * smaller one. */ - mf.width = VIN_MAX_WIDTH; - mf.height = VIN_MAX_HEIGHT; + mf->width = VIN_MAX_WIDTH; + mf->height = VIN_MAX_HEIGHT; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - video, try_mbus_fmt, - &mf); + pad, set_fmt, &pad_cfg, + &format); if (ret < 0) { dev_err(icd->parent, "client try_fmt() = %d\n", ret); @@ -1748,9 +1780,9 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd, } } /* We will scale exactly */ - if (mf.width > width) + if (mf->width > width) pix->width = width; - if (mf.height > height) + if (mf->height > height) pix->height = height; } @@ -1770,6 +1802,7 @@ static int rcar_vin_querycap(struct soc_camera_host *ici, strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr); return 0; } @@ -1808,7 +1841,7 @@ static struct soc_camera_host_ops rcar_vin_host_ops = { }; #ifdef CONFIG_OF -static struct of_device_id rcar_vin_of_table[] = { +static const struct of_device_id rcar_vin_of_table[] = { { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, @@ -1821,8 +1854,6 @@ MODULE_DEVICE_TABLE(of, rcar_vin_of_table); #endif static struct platform_device_id rcar_vin_id_table[] = { - { "r8a7791-vin", RCAR_GEN2 }, - { "r8a7790-vin", RCAR_GEN2 }, { "r8a7779-vin", RCAR_H1 }, { "r8a7778-vin", RCAR_M1 }, { "uPD35004-vin", RCAR_E1 }, diff --git a/kernel/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/kernel/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 9ce202f53..67a669d82 100644 --- a/kernel/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/kernel/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -93,7 +93,7 @@ /* per video frame buffer */ struct sh_mobile_ceu_buffer { - struct vb2_buffer vb; /* v4l buffer must be first */ + struct vb2_v4l2_buffer vb; /* v4l buffer must be first */ struct list_head queue; }; @@ -112,7 +112,7 @@ struct sh_mobile_ceu_dev { spinlock_t lock; /* Protects video buffer lists */ struct list_head capture; - struct vb2_buffer *active; + struct vb2_v4l2_buffer *active; struct vb2_alloc_ctx *alloc_ctx; struct sh_mobile_ceu_info *pdata; @@ -152,9 +152,9 @@ struct sh_mobile_ceu_cam { u32 code; }; -static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb) +static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_v4l2_buffer *vbuf) { - return container_of(vb, struct sh_mobile_ceu_buffer, vb); + return container_of(vbuf, struct sh_mobile_ceu_buffer, vb); } static void ceu_write(struct sh_mobile_ceu_dev *priv, @@ -210,11 +210,13 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) * for the current frame format if required */ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); + const struct v4l2_format *fmt = parg; + struct soc_camera_device *icd = container_of(vq, + struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -334,7 +336,8 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) bottom2 = CDBCR; } - phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0); + phys_addr_top = + vb2_dma_contig_plane_dma_addr(&pcdev->active->vb2_buf, 0); switch (icd->current_fmt->host_fmt->fourcc) { case V4L2_PIX_FMT_NV12: @@ -369,7 +372,8 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) { - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); /* Added list head initialization on alloc */ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); @@ -379,17 +383,19 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) { - struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); unsigned long size; size = icd->sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", - vb->v4l2_buf.index, vb2_plane_size(vb, 0), size); + vb->index, vb2_plane_size(vb, 0), size); goto error; } @@ -416,7 +422,7 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) * we are not interested in the return value of * sh_mobile_ceu_capture here. */ - pcdev->active = vb; + pcdev->active = vbuf; sh_mobile_ceu_capture(pcdev); } spin_unlock_irq(&pcdev->lock); @@ -429,14 +435,16 @@ error: static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) { - struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); struct sh_mobile_ceu_dev *pcdev = ici->priv; spin_lock_irq(&pcdev->lock); - if (pcdev->active == vb) { + if (pcdev->active == vbuf) { /* disable capture (release DMA buffer), reset */ ceu_write(pcdev, CAPSR, 1 << 16); pcdev->active = NULL; @@ -458,7 +466,9 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) { - struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -467,7 +477,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) pcdev->buf_total); /* This is for locking debugging only */ - INIT_LIST_HEAD(&to_ceu_vb(vb)->queue); + INIT_LIST_HEAD(&to_ceu_vb(vbuf)->queue); return 0; } @@ -504,17 +514,17 @@ static struct vb2_ops sh_mobile_ceu_videobuf_ops = { static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) { struct sh_mobile_ceu_dev *pcdev = data; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; int ret; spin_lock(&pcdev->lock); - vb = pcdev->active; - if (!vb) + vbuf = pcdev->active; + if (!vbuf) /* Stale interrupt from a released buffer */ goto out; - list_del_init(&to_ceu_vb(vb)->queue); + list_del_init(&to_ceu_vb(vbuf)->queue); if (!list_empty(&pcdev->capture)) pcdev->active = &list_entry(pcdev->capture.next, @@ -523,12 +533,13 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) pcdev->active = NULL; ret = sh_mobile_ceu_capture(pcdev); - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vbuf->timestamp); if (!ret) { - vb->v4l2_buf.field = pcdev->field; - vb->v4l2_buf.sequence = pcdev->sequence++; + vbuf->field = pcdev->field; + vbuf->sequence = pcdev->sequence++; } - vb2_buffer_done(vb, ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + vb2_buffer_done(&vbuf->vb2_buf, + ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); out: spin_unlock(&pcdev->lock); @@ -633,7 +644,7 @@ static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici) spin_lock_irq(&pcdev->lock); if (pcdev->active) { list_del_init(&to_ceu_vb(pcdev->active)->queue); - vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&pcdev->active->vb2_buf, VB2_BUF_STATE_ERROR); pcdev->active = NULL; } spin_unlock_irq(&pcdev->lock); @@ -1048,17 +1059,20 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int int ret, k, n; int formats = 0; struct sh_mobile_ceu_cam *cam; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; const struct soc_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code); + fmt = soc_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); return 0; } @@ -1070,7 +1084,10 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int } if (!icd->host_priv) { - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; struct v4l2_rect rect; int shift = 0; @@ -1088,7 +1105,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int return ret; /* First time */ - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; @@ -1099,14 +1116,14 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int * sizes, just try VGA multiples. If needed, this can be * adjusted in the future. */ - while ((mf.width > pcdev->max_width || - mf.height > pcdev->max_height) && shift < 4) { + while ((mf->width > pcdev->max_width || + mf->height > pcdev->max_height) && shift < 4) { /* Try 2560x1920, 1280x960, 640x480, 320x240 */ - mf.width = 2560 >> shift; - mf.height = 1920 >> shift; + mf->width = 2560 >> shift; + mf->height = 1920 >> shift; ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, &mf); + soc_camera_grp_id(icd), pad, + set_fmt, NULL, &fmt); if (ret < 0) return ret; shift++; @@ -1114,11 +1131,11 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int if (shift == 4) { dev_err(dev, "Failed to configure the client below %ux%x\n", - mf.width, mf.height); + mf->width, mf->height); return -EIO; } - dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); + dev_geo(dev, "camera fmt %ux%u\n", mf->width, mf->height); cam = kzalloc(sizeof(*cam), GFP_KERNEL); if (!cam) @@ -1128,8 +1145,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int cam->rect = rect; cam->subrect = rect; - cam->width = mf.width; - cam->height = mf.height; + cam->width = mf->width; + cam->height = mf->height; icd->host_priv = cam; } else { @@ -1140,7 +1157,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int if (!idx) cam->extra_fmt = NULL; - switch (code) { + switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -1163,10 +1180,10 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int formats += n; for (k = 0; xlate && k < n; k++) { xlate->host_fmt = &sh_mobile_ceu_formats[k]; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "Providing format %s using code %d\n", - sh_mobile_ceu_formats[k].name, code); + sh_mobile_ceu_formats[k].name, code.code); } break; default: @@ -1178,7 +1195,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int formats++; if (xlate) { xlate->host_fmt = fmt; - xlate->code = code; + xlate->code = code.code; xlate++; dev_dbg(dev, "Providing format %s in pass-through mode\n", fmt->name); @@ -1214,7 +1231,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_rect *cam_rect = &cam_crop.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, out_width, out_height; int interm_width, interm_height; @@ -1244,16 +1264,16 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, /* On success cam_crop contains current camera crop */ /* 3. Retrieve camera output window */ - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) return ret; - if (mf.width > pcdev->max_width || mf.height > pcdev->max_height) + if (mf->width > pcdev->max_width || mf->height > pcdev->max_height) return -EINVAL; /* 4. Calculate camera scales */ - scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); + scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); + scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); /* Calculate intermediate window */ interm_width = scale_down(rect->width, scale_cam_h); @@ -1264,7 +1284,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, new_scale_h = calc_generic_scale(rect->width, icd->user_width); - mf.width = scale_down(cam_rect->width, new_scale_h); + mf->width = scale_down(cam_rect->width, new_scale_h); } if (interm_height < icd->user_height) { @@ -1272,26 +1292,26 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, new_scale_v = calc_generic_scale(rect->height, icd->user_height); - mf.height = scale_down(cam_rect->height, new_scale_v); + mf->height = scale_down(cam_rect->height, new_scale_v); } if (interm_width < icd->user_width || interm_height < icd->user_height) { ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, &mf); + soc_camera_grp_id(icd), pad, + set_fmt, NULL, &fmt); if (ret < 0) return ret; - dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height); - scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); + dev_geo(dev, "New camera output %ux%u\n", mf->width, mf->height); + scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); + scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); interm_width = scale_down(rect->width, scale_cam_h); interm_height = scale_down(rect->height, scale_cam_v); } /* Cache camera output window */ - cam->width = mf.width; - cam->height = mf.height; + cam->width = mf->width; + cam->height = mf->height; if (pcdev->image_mode) { out_width = min(interm_width, icd->user_width); @@ -1490,7 +1510,11 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; __u32 pixfmt = pix->pixelformat; int width, height; int ret; @@ -1518,21 +1542,21 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, height = pix->height; /* limit to sensor capabilities */ - mf.width = pix->width; - mf.height = pix->height; - mf.field = pix->field; - mf.code = xlate->code; - mf.colorspace = pix->colorspace; + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->code = xlate->code; + mf->colorspace = pix->colorspace; ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - video, try_mbus_fmt, &mf); + pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf.width; - pix->height = mf.height; - pix->field = mf.field; - pix->colorspace = mf.colorspace; + pix->width = mf->width; + pix->height = mf->height; + pix->field = mf->field; + pix->colorspace = mf->colorspace; switch (pixfmt) { case V4L2_PIX_FMT_NV12: @@ -1547,11 +1571,11 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, * requested a bigger rectangle, it will not return a * smaller one. */ - mf.width = pcdev->max_width; - mf.height = pcdev->max_height; + mf->width = pcdev->max_width; + mf->height = pcdev->max_height; ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - try_mbus_fmt, &mf); + soc_camera_grp_id(icd), pad, + set_fmt, &pad_cfg, &format); if (ret < 0) { /* Shouldn't actually happen... */ dev_err(icd->parent, @@ -1560,9 +1584,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, } } /* We will scale exactly */ - if (mf.width > width) + if (mf->width > width) pix->width = width; - if (mf.height > height) + if (mf->height > height) pix->height = height; pix->bytesperline = max(pix->bytesperline, pix->width); @@ -1652,6 +1676,8 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, struct v4l2_capability *cap) { strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); + strlcpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -1760,6 +1786,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->max_height = pcdev->pdata->max_height; pcdev->flags = pcdev->pdata->flags; } + pcdev->field = V4L2_FIELD_NONE; if (!pcdev->max_width) { unsigned int v; diff --git a/kernel/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/kernel/drivers/media/platform/soc_camera/sh_mobile_csi2.c index cd93241eb..12d3626ec 100644 --- a/kernel/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/kernel/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -45,11 +45,17 @@ struct sh_csi2 { static void sh_csi2_hwinit(struct sh_csi2 *priv); -static int sh_csi2_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int sh_csi2_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + struct v4l2_mbus_framefmt *mf = &format->format; + u32 tmp = (priv->client->channel & 3) << 8; + + if (format->pad) + return -EINVAL; if (mf->width > 8188) mf->width = 8188; @@ -85,21 +91,11 @@ static int sh_csi2_try_fmt(struct v4l2_subdev *sd, break; } - return 0; -} - -/* - * We have done our best in try_fmt to try and tell the sensor, which formats - * we support. If now the configuration is unsuitable for us we can only - * error out. - */ -static int sh_csi2_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - u32 tmp = (priv->client->channel & 3) << 8; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); if (mf->width > 8188 || mf->width & 1) return -EINVAL; @@ -211,12 +207,14 @@ static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd, } static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { - .s_mbus_fmt = sh_csi2_s_fmt, - .try_mbus_fmt = sh_csi2_try_fmt, .g_mbus_config = sh_csi2_g_mbus_config, .s_mbus_config = sh_csi2_s_mbus_config, }; +static struct v4l2_subdev_pad_ops sh_csi2_subdev_pad_ops = { + .set_fmt = sh_csi2_set_fmt, +}; + static void sh_csi2_hwinit(struct sh_csi2 *priv) { struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; @@ -313,6 +311,7 @@ static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = { static struct v4l2_subdev_ops sh_csi2_subdev_ops = { .core = &sh_csi2_subdev_core_ops, .video = &sh_csi2_subdev_video_ops, + .pad = &sh_csi2_subdev_pad_ops, }; static int sh_csi2_probe(struct platform_device *pdev) diff --git a/kernel/drivers/media/platform/soc_camera/soc_camera.c b/kernel/drivers/media/platform/soc_camera/soc_camera.c index 7bfe76656..dc98122e7 100644 --- a/kernel/drivers/media/platform/soc_camera/soc_camera.c +++ b/kernel/drivers/media/platform/soc_camera/soc_camera.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include /* Default to VGA resolution */ #define DEFAULT_WIDTH 640 @@ -309,11 +309,14 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, static int soc_camera_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct soc_camera_device *icd = file->private_data; + if (inp->index != 0) return -EINVAL; /* default is camera */ inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = icd->vdev->tvnorms; strcpy(inp->name, "Camera"); return 0; @@ -381,9 +384,8 @@ static int soc_camera_reqbufs(struct file *file, void *priv, ret = vb2_reqbufs(&icd->vb2_vidq, p); } - if (!ret && !icd->streamer) - icd->streamer = file; - + if (!ret) + icd->streamer = p->count ? file : NULL; return ret; } @@ -440,12 +442,19 @@ static int soc_camera_create_bufs(struct file *file, void *priv, { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; /* videobuf2 only */ if (ici->ops->init_videobuf) - return -EINVAL; - else - return vb2_create_bufs(&icd->vb2_vidq, create); + return -ENOTTY; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_create_bufs(&icd->vb2_vidq, create); + if (!ret) + icd->streamer = file; + return ret; } static int soc_camera_prepare_buf(struct file *file, void *priv, @@ -467,14 +476,13 @@ static int soc_camera_expbuf(struct file *file, void *priv, struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - if (icd->streamer != file) - return -EBUSY; - /* videobuf2 only */ if (ici->ops->init_videobuf) - return -EINVAL; - else - return vb2_expbuf(&icd->vb2_vidq, p); + return -ENOTTY; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + return vb2_expbuf(&icd->vb2_vidq, p); } /* Always entered with .host_lock held */ @@ -484,10 +492,14 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); unsigned int i, fmts = 0, raw_fmts = 0; int ret; - u32 code; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code)) + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { raw_fmts++; + code.index++; + } if (!ici->ops->get_formats) /* @@ -521,11 +533,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) fmts = 0; for (i = 0; i < raw_fmts; i++) if (!ici->ops->get_formats) { - v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code); + code.index = i; + v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); icd->user_formats[fmts].host_fmt = - soc_mbus_get_fmtdesc(code); + soc_mbus_get_fmtdesc(code.code); if (icd->user_formats[fmts].host_fmt) - icd->user_formats[fmts++].code = code; + icd->user_formats[fmts++].code = code.code; } else { ret = ici->ops->get_formats(icd, i, &icd->user_formats[fmts]); @@ -775,20 +788,21 @@ static int soc_camera_close(struct file *file) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); mutex_lock(&ici->host_lock); + if (icd->streamer == file) { + if (ici->ops->init_videobuf2) + vb2_queue_release(&icd->vb2_vidq); + icd->streamer = NULL; + } icd->use_count--; if (!icd->use_count) { pm_runtime_suspend(&icd->vdev->dev); pm_runtime_disable(&icd->vdev->dev); - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); __soc_camera_power_off(icd); soc_camera_remove_device(icd); } - if (icd->streamer == file) - icd->streamer = NULL; mutex_unlock(&ici->host_lock); module_put(ici->ops->owner); @@ -987,6 +1001,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; WARN_ON(priv != file->private_data); @@ -1001,13 +1016,13 @@ static int soc_camera_streamoff(struct file *file, void *priv, * remaining buffers. When the last buffer is freed, stop capture */ if (ici->ops->init_videobuf) - videobuf_streamoff(&icd->vb_vidq); + ret = videobuf_streamoff(&icd->vb_vidq); else - vb2_streamoff(&icd->vb2_vidq, i); + ret = vb2_streamoff(&icd->vb2_vidq, i); v4l2_subdev_call(sd, video, s_stream, 0); - return 0; + return ret; } static int soc_camera_cropcap(struct file *file, void *fh, @@ -1284,7 +1299,10 @@ static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_cli static int soc_camera_probe_finish(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_framefmt mf; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; sd->grp_id = soc_camera_grp_id(icd); @@ -1314,11 +1332,11 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd) goto evidstart; /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) { - icd->user_width = mf.width; - icd->user_height = mf.height; - icd->colorspace = mf.colorspace; - icd->field = mf.field; + if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { + icd->user_width = mf->width; + icd->user_height = mf->height; + icd->colorspace = mf->colorspace; + icd->field = mf->field; } soc_camera_remove_device(icd); @@ -1613,7 +1631,7 @@ static int soc_of_bind(struct soc_camera_host *ici, struct soc_camera_async_client *sasc; struct soc_of_info *info; struct i2c_client *client; - char clk_name[V4L2_SUBDEV_NAME_SIZE]; + char clk_name[V4L2_SUBDEV_NAME_SIZE + 32]; int ret; /* allocate a new subdev and add match info to it */ diff --git a/kernel/drivers/media/platform/soc_camera/soc_camera_platform.c b/kernel/drivers/media/platform/soc_camera/soc_camera_platform.c index f535910b4..cc8eb0758 100644 --- a/kernel/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/kernel/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -37,9 +37,11 @@ static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) } static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &format->format; mf->width = p->format.width; mf->height = p->format.height; @@ -61,15 +63,16 @@ static struct v4l2_subdev_core_ops platform_subdev_core_ops = { .s_power = soc_camera_platform_s_power, }; -static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - if (index) + if (code->pad || code->index) return -EINVAL; - *code = p->format.code; + code->code = p->format.code; return 0; } @@ -117,18 +120,21 @@ static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, - .enum_mbus_fmt = soc_camera_platform_enum_fmt, .cropcap = soc_camera_platform_cropcap, .g_crop = soc_camera_platform_g_crop, - .try_mbus_fmt = soc_camera_platform_fill_fmt, - .g_mbus_fmt = soc_camera_platform_fill_fmt, - .s_mbus_fmt = soc_camera_platform_fill_fmt, .g_mbus_config = soc_camera_platform_g_mbus_config, }; +static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = { + .enum_mbus_code = soc_camera_platform_enum_mbus_code, + .get_fmt = soc_camera_platform_fill_fmt, + .set_fmt = soc_camera_platform_fill_fmt, +}; + static struct v4l2_subdev_ops platform_subdev_ops = { .core = &platform_subdev_core_ops, .video = &platform_subdev_video_ops, + .pad = &platform_subdev_pad_ops, }; static int soc_camera_platform_probe(struct platform_device *pdev) diff --git a/kernel/drivers/media/platform/soc_camera/soc_scale_crop.c b/kernel/drivers/media/platform/soc_camera/soc_scale_crop.c index 8e74fb7f2..bda29bc1b 100644 --- a/kernel/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/kernel/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -211,22 +211,23 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, } EXPORT_SYMBOL(soc_camera_client_s_crop); -/* Iterative s_mbus_fmt, also updates cached client crop on success */ -static int client_s_fmt(struct soc_camera_device *icd, +/* Iterative set_fmt, also updates cached client crop on success */ +static int client_set_fmt(struct soc_camera_device *icd, struct v4l2_rect *rect, struct v4l2_rect *subrect, unsigned int max_width, unsigned int max_height, - struct v4l2_mbus_framefmt *mf, bool host_can_scale) + struct v4l2_subdev_format *format, bool host_can_scale) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; + struct v4l2_mbus_framefmt *mf = &format->format; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; struct v4l2_cropcap cap; bool host_1to1; int ret; ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, mf); + soc_camera_grp_id(icd), pad, + set_fmt, NULL, format); if (ret < 0) return ret; @@ -265,8 +266,8 @@ static int client_s_fmt(struct soc_camera_device *icd, mf->width = tmp_w; mf->height = tmp_h; ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, mf); + soc_camera_grp_id(icd), pad, + set_fmt, NULL, format); dev_geo(dev, "Camera scaled to %ux%u\n", mf->width, mf->height); if (ret < 0) { @@ -309,7 +310,11 @@ int soc_camera_client_scale(struct soc_camera_device *icd, bool host_can_scale, unsigned int shift) { struct device *dev = icd->parent; - struct v4l2_mbus_framefmt mf_tmp = *mf; + struct v4l2_subdev_format fmt_tmp = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format = *mf, + }; + struct v4l2_mbus_framefmt *mf_tmp = &fmt_tmp.format; unsigned int scale_h, scale_v; int ret; @@ -317,25 +322,25 @@ int soc_camera_client_scale(struct soc_camera_device *icd, * 5. Apply iterative camera S_FMT for camera user window (also updates * client crop cache and the imaginary sub-rectangle). */ - ret = client_s_fmt(icd, rect, subrect, *width, *height, - &mf_tmp, host_can_scale); + ret = client_set_fmt(icd, rect, subrect, *width, *height, + &fmt_tmp, host_can_scale); if (ret < 0) return ret; dev_geo(dev, "5: camera scaled to %ux%u\n", - mf_tmp.width, mf_tmp.height); + mf_tmp->width, mf_tmp->height); /* 6. Retrieve camera output window (g_fmt) */ /* unneeded - it is already in "mf_tmp" */ /* 7. Calculate new client scales. */ - scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width); - scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height); + scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp->width); + scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp->height); - mf->width = mf_tmp.width; - mf->height = mf_tmp.height; - mf->colorspace = mf_tmp.colorspace; + mf->width = mf_tmp->width; + mf->height = mf_tmp->height; + mf->colorspace = mf_tmp->colorspace; /* * 8. Calculate new host crop - apply camera scales to previously diff --git a/kernel/drivers/media/platform/sti/bdisp/Makefile b/kernel/drivers/media/platform/sti/bdisp/Makefile new file mode 100644 index 000000000..bc53496fa --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_STI_BDISP) := bdisp.o + +bdisp-objs := bdisp-v4l2.o bdisp-hw.o bdisp-debug.o diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp-debug.c b/kernel/drivers/media/platform/sti/bdisp/bdisp-debug.c new file mode 100644 index 000000000..79c56356a --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -0,0 +1,687 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include + +#include "bdisp.h" +#include "bdisp-filter.h" +#include "bdisp-reg.h" + +void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp) +{ + bdisp->dbg.hw_start = ktime_get(); +} + +void bdisp_dbg_perf_end(struct bdisp_dev *bdisp) +{ + s64 time_us; + + time_us = ktime_us_delta(ktime_get(), bdisp->dbg.hw_start); + + if (!bdisp->dbg.min_duration) + bdisp->dbg.min_duration = time_us; + else + bdisp->dbg.min_duration = min(time_us, bdisp->dbg.min_duration); + + bdisp->dbg.last_duration = time_us; + bdisp->dbg.max_duration = max(time_us, bdisp->dbg.max_duration); + bdisp->dbg.tot_duration += time_us; +} + +static void bdisp_dbg_dump_ins(struct seq_file *s, u32 val) +{ + seq_printf(s, "INS\t0x%08X\t", val); + + switch (val & BLT_INS_S1_MASK) { + case BLT_INS_S1_OFF: + break; + case BLT_INS_S1_MEM: + seq_puts(s, "SRC1=mem - "); + break; + case BLT_INS_S1_CF: + seq_puts(s, "SRC1=ColorFill - "); + break; + case BLT_INS_S1_COPY: + seq_puts(s, "SRC1=copy - "); + break; + case BLT_INS_S1_FILL: + seq_puts(s, "SRC1=fil - "); + break; + default: + seq_puts(s, "SRC1=??? - "); + break; + } + + switch (val & BLT_INS_S2_MASK) { + case BLT_INS_S2_OFF: + break; + case BLT_INS_S2_MEM: + seq_puts(s, "SRC2=mem - "); + break; + case BLT_INS_S2_CF: + seq_puts(s, "SRC2=ColorFill - "); + break; + default: + seq_puts(s, "SRC2=??? - "); + break; + } + + if ((val & BLT_INS_S3_MASK) == BLT_INS_S3_MEM) + seq_puts(s, "SRC3=mem - "); + + if (val & BLT_INS_IVMX) + seq_puts(s, "IVMX - "); + if (val & BLT_INS_CLUT) + seq_puts(s, "CLUT - "); + if (val & BLT_INS_SCALE) + seq_puts(s, "Scale - "); + if (val & BLT_INS_FLICK) + seq_puts(s, "Flicker - "); + if (val & BLT_INS_CLIP) + seq_puts(s, "Clip - "); + if (val & BLT_INS_CKEY) + seq_puts(s, "ColorKey - "); + if (val & BLT_INS_OVMX) + seq_puts(s, "OVMX - "); + if (val & BLT_INS_DEI) + seq_puts(s, "Deint - "); + if (val & BLT_INS_PMASK) + seq_puts(s, "PlaneMask - "); + if (val & BLT_INS_VC1R) + seq_puts(s, "VC1R - "); + if (val & BLT_INS_ROTATE) + seq_puts(s, "Rotate - "); + if (val & BLT_INS_GRAD) + seq_puts(s, "GradFill - "); + if (val & BLT_INS_AQLOCK) + seq_puts(s, "AQLock - "); + if (val & BLT_INS_PACE) + seq_puts(s, "Pace - "); + if (val & BLT_INS_IRQ) + seq_puts(s, "IRQ - "); + + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val) +{ + seq_printf(s, "TTY\t0x%08X\t", val); + seq_printf(s, "Pitch=%d - ", val & 0xFFFF); + + switch ((val & BLT_TTY_COL_MASK) >> BLT_TTY_COL_SHIFT) { + case BDISP_RGB565: + seq_puts(s, "RGB565 - "); + break; + case BDISP_RGB888: + seq_puts(s, "RGB888 - "); + break; + case BDISP_XRGB8888: + seq_puts(s, "xRGB888 - "); + break; + case BDISP_ARGB8888: + seq_puts(s, "ARGB8888 - "); + break; + case BDISP_NV12: + seq_puts(s, "NV12 - "); + break; + case BDISP_YUV_3B: + seq_puts(s, "YUV420P - "); + break; + default: + seq_puts(s, "ColorFormat ??? - "); + break; + } + + if (val & BLT_TTY_ALPHA_R) + seq_puts(s, "AlphaRange - "); + if (val & BLT_TTY_CR_NOT_CB) + seq_puts(s, "CrNotCb - "); + if (val & BLT_TTY_MB) + seq_puts(s, "MB - "); + if (val & BLT_TTY_HSO) + seq_puts(s, "HSO inverse - "); + if (val & BLT_TTY_VSO) + seq_puts(s, "VSO inverse - "); + if (val & BLT_TTY_DITHER) + seq_puts(s, "Dither - "); + if (val & BLT_TTY_CHROMA) + seq_puts(s, "Write CHROMA - "); + if (val & BLT_TTY_BIG_END) + seq_puts(s, "BigEndian - "); + + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_xy(struct seq_file *s, u32 val, char *name) +{ + seq_printf(s, "%s\t0x%08X\t", name, val); + seq_printf(s, "(%d,%d)\n", val & 0xFFFF, (val >> 16)); +} + +static void bdisp_dbg_dump_sz(struct seq_file *s, u32 val, char *name) +{ + seq_printf(s, "%s\t0x%08X\t", name, val); + seq_printf(s, "%dx%d\n", val & 0x1FFF, (val >> 16) & 0x1FFF); +} + +static void bdisp_dbg_dump_sty(struct seq_file *s, + u32 val, u32 addr, char *name) +{ + bool s1, s2, s3; + + seq_printf(s, "%s\t0x%08X\t", name, val); + + if (!addr || !name || (strlen(name) < 2)) + goto done; + + s1 = name[strlen(name) - 1] == '1'; + s2 = name[strlen(name) - 1] == '2'; + s3 = name[strlen(name) - 1] == '3'; + + seq_printf(s, "Pitch=%d - ", val & 0xFFFF); + + switch ((val & BLT_TTY_COL_MASK) >> BLT_TTY_COL_SHIFT) { + case BDISP_RGB565: + seq_puts(s, "RGB565 - "); + break; + case BDISP_RGB888: + seq_puts(s, "RGB888 - "); + break; + case BDISP_XRGB8888: + seq_puts(s, "xRGB888 - "); + break; + case BDISP_ARGB8888: + seq_puts(s, "ARGB888 - "); + break; + case BDISP_NV12: + seq_puts(s, "NV12 - "); + break; + case BDISP_YUV_3B: + seq_puts(s, "YUV420P - "); + break; + default: + seq_puts(s, "ColorFormat ??? - "); + break; + } + + if ((val & BLT_TTY_ALPHA_R) && !s3) + seq_puts(s, "AlphaRange - "); + if ((val & BLT_S1TY_A1_SUBSET) && !s3) + seq_puts(s, "A1SubSet - "); + if ((val & BLT_TTY_MB) && !s1) + seq_puts(s, "MB - "); + if (val & BLT_TTY_HSO) + seq_puts(s, "HSO inverse - "); + if (val & BLT_TTY_VSO) + seq_puts(s, "VSO inverse - "); + if ((val & BLT_S1TY_CHROMA_EXT) && (s1 || s2)) + seq_puts(s, "ChromaExt - "); + if ((val & BLT_S3TY_BLANK_ACC) && s3) + seq_puts(s, "Blank Acc - "); + if ((val & BTL_S1TY_SUBBYTE) && !s3) + seq_puts(s, "SubByte - "); + if ((val & BLT_S1TY_RGB_EXP) && !s3) + seq_puts(s, "RGBExpand - "); + if ((val & BLT_TTY_BIG_END) && !s3) + seq_puts(s, "BigEndian - "); + +done: + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val) +{ + seq_printf(s, "FCTL\t0x%08X\t", val); + + if ((val & BLT_FCTL_Y_HV_SCALE) == BLT_FCTL_Y_HV_SCALE) + seq_puts(s, "Resize Luma - "); + else if ((val & BLT_FCTL_Y_HV_SCALE) == BLT_FCTL_Y_HV_SAMPLE) + seq_puts(s, "Sample Luma - "); + + if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SCALE) + seq_puts(s, "Resize Chroma"); + else if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SAMPLE) + seq_puts(s, "Sample Chroma"); + + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name) +{ + u32 inc; + + seq_printf(s, "%s\t0x%08X\t", name, val); + + if (!val) + goto done; + + inc = val & 0xFFFF; + seq_printf(s, "H: %d(6.10) / scale~%dx0.1 - ", inc, 1024 * 10 / inc); + + inc = val >> 16; + seq_printf(s, "V: %d(6.10) / scale~%dx0.1", inc, 1024 * 10 / inc); + +done: + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name) +{ + seq_printf(s, "%s\t0x%08X\t", name, val); + + if (!val) + goto done; + + seq_printf(s, "H: init=%d repeat=%d - ", val & 0x3FF, (val >> 12) & 7); + val >>= 16; + seq_printf(s, "V: init=%d repeat=%d", val & 0x3FF, (val >> 12) & 7); + +done: + seq_puts(s, "\n"); +} + +static void bdisp_dbg_dump_ivmx(struct seq_file *s, + u32 c0, u32 c1, u32 c2, u32 c3) +{ + seq_printf(s, "IVMX0\t0x%08X\n", c0); + seq_printf(s, "IVMX1\t0x%08X\n", c1); + seq_printf(s, "IVMX2\t0x%08X\n", c2); + seq_printf(s, "IVMX3\t0x%08X\t", c3); + + if (!c0 && !c1 && !c2 && !c3) { + seq_puts(s, "\n"); + return; + } + + if ((c0 == bdisp_rgb_to_yuv[0]) && + (c1 == bdisp_rgb_to_yuv[1]) && + (c2 == bdisp_rgb_to_yuv[2]) && + (c3 == bdisp_rgb_to_yuv[3])) { + seq_puts(s, "RGB to YUV\n"); + return; + } + + if ((c0 == bdisp_yuv_to_rgb[0]) && + (c1 == bdisp_yuv_to_rgb[1]) && + (c2 == bdisp_yuv_to_rgb[2]) && + (c3 == bdisp_yuv_to_rgb[3])) { + seq_puts(s, "YUV to RGB\n"); + return; + } + seq_puts(s, "Unknown conversion\n"); +} + +static int bdisp_dbg_last_nodes(struct seq_file *s, void *data) +{ + /* Not dumping all fields, focusing on significant ones */ + struct bdisp_dev *bdisp = s->private; + struct bdisp_node *node; + int i = 0; + + if (!bdisp->dbg.copy_node[0]) { + seq_puts(s, "No node built yet\n"); + return 0; + } + + do { + node = bdisp->dbg.copy_node[i]; + if (!node) + break; + seq_printf(s, "--------\nNode %d:\n", i); + seq_puts(s, "-- General --\n"); + seq_printf(s, "NIP\t0x%08X\n", node->nip); + seq_printf(s, "CIC\t0x%08X\n", node->cic); + bdisp_dbg_dump_ins(s, node->ins); + seq_printf(s, "ACK\t0x%08X\n", node->ack); + seq_puts(s, "-- Target --\n"); + seq_printf(s, "TBA\t0x%08X\n", node->tba); + bdisp_dbg_dump_tty(s, node->tty); + bdisp_dbg_dump_xy(s, node->txy, "TXY"); + bdisp_dbg_dump_sz(s, node->tsz, "TSZ"); + /* Color Fill not dumped */ + seq_puts(s, "-- Source 1 --\n"); + seq_printf(s, "S1BA\t0x%08X\n", node->s1ba); + bdisp_dbg_dump_sty(s, node->s1ty, node->s1ba, "S1TY"); + bdisp_dbg_dump_xy(s, node->s1xy, "S1XY"); + seq_puts(s, "-- Source 2 --\n"); + seq_printf(s, "S2BA\t0x%08X\n", node->s2ba); + bdisp_dbg_dump_sty(s, node->s2ty, node->s2ba, "S2TY"); + bdisp_dbg_dump_xy(s, node->s2xy, "S2XY"); + bdisp_dbg_dump_sz(s, node->s2sz, "S2SZ"); + seq_puts(s, "-- Source 3 --\n"); + seq_printf(s, "S3BA\t0x%08X\n", node->s3ba); + bdisp_dbg_dump_sty(s, node->s3ty, node->s3ba, "S3TY"); + bdisp_dbg_dump_xy(s, node->s3xy, "S3XY"); + bdisp_dbg_dump_sz(s, node->s3sz, "S3SZ"); + /* Clipping not dumped */ + /* CLUT not dumped */ + seq_puts(s, "-- Filter & Mask --\n"); + bdisp_dbg_dump_fctl(s, node->fctl); + /* PMK not dumped */ + seq_puts(s, "-- Chroma Filter --\n"); + bdisp_dbg_dump_rsf(s, node->rsf, "RSF"); + bdisp_dbg_dump_rzi(s, node->rzi, "RZI"); + seq_printf(s, "HFP\t0x%08X\n", node->hfp); + seq_printf(s, "VFP\t0x%08X\n", node->vfp); + seq_puts(s, "-- Luma Filter --\n"); + bdisp_dbg_dump_rsf(s, node->y_rsf, "Y_RSF"); + bdisp_dbg_dump_rzi(s, node->y_rzi, "Y_RZI"); + seq_printf(s, "Y_HFP\t0x%08X\n", node->y_hfp); + seq_printf(s, "Y_VFP\t0x%08X\n", node->y_vfp); + /* Flicker not dumped */ + /* Color key not dumped */ + /* Reserved not dumped */ + /* Static Address & User not dumped */ + seq_puts(s, "-- Input Versatile Matrix --\n"); + bdisp_dbg_dump_ivmx(s, node->ivmx0, node->ivmx1, + node->ivmx2, node->ivmx3); + /* Output Versatile Matrix not dumped */ + /* Pace not dumped */ + /* VC1R & DEI not dumped */ + /* Gradient Fill not dumped */ + } while ((++i < MAX_NB_NODE) && node->nip); + + return 0; +} + +static int bdisp_dbg_last_nodes_raw(struct seq_file *s, void *data) +{ + struct bdisp_dev *bdisp = s->private; + struct bdisp_node *node; + u32 *val; + int j, i = 0; + + if (!bdisp->dbg.copy_node[0]) { + seq_puts(s, "No node built yet\n"); + return 0; + } + + do { + node = bdisp->dbg.copy_node[i]; + if (!node) + break; + + seq_printf(s, "--------\nNode %d:\n", i); + val = (u32 *)node; + for (j = 0; j < sizeof(struct bdisp_node) / sizeof(u32); j++) + seq_printf(s, "0x%08X\n", *val++); + } while ((++i < MAX_NB_NODE) && node->nip); + + return 0; +} + +static const char *bdisp_fmt_to_str(struct bdisp_frame frame) +{ + switch (frame.fmt->pixelformat) { + case V4L2_PIX_FMT_YUV420: + return "YUV420P"; + case V4L2_PIX_FMT_NV12: + if (frame.field == V4L2_FIELD_INTERLACED) + return "NV12 interlaced"; + else + return "NV12"; + case V4L2_PIX_FMT_RGB565: + return "RGB16"; + case V4L2_PIX_FMT_RGB24: + return "RGB24"; + case V4L2_PIX_FMT_XBGR32: + return "XRGB"; + case V4L2_PIX_FMT_ABGR32: + return "ARGB"; + default: + return "????"; + } +} + +static int bdisp_dbg_last_request(struct seq_file *s, void *data) +{ + struct bdisp_dev *bdisp = s->private; + struct bdisp_request *request = &bdisp->dbg.copy_request; + struct bdisp_frame src, dst; + + if (!request->nb_req) { + seq_puts(s, "No request\n"); + return 0; + } + + src = request->src; + dst = request->dst; + + seq_printf(s, "\nRequest #%d\n", request->nb_req); + + seq_printf(s, "Format: %s\t\t\t%s\n", + bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst)); + seq_printf(s, "Crop area: %dx%d @ %d,%d ==>\t%dx%d @ %d,%d\n", + src.crop.width, src.crop.height, + src.crop.left, src.crop.top, + dst.crop.width, dst.crop.height, + dst.crop.left, dst.crop.top); + seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n", + src.width, src.height, dst.width, dst.height); + + if (request->hflip) + seq_puts(s, "Horizontal flip\n\n"); + + if (request->vflip) + seq_puts(s, "Vertical flip\n\n"); + + return 0; +} + +#define DUMP(reg) seq_printf(s, #reg " \t0x%08X\n", readl(bdisp->regs + reg)) + +static int bdisp_dbg_regs(struct seq_file *s, void *data) +{ + struct bdisp_dev *bdisp = s->private; + int ret; + unsigned int i; + + ret = pm_runtime_get_sync(bdisp->dev); + if (ret < 0) { + seq_puts(s, "Cannot wake up IP\n"); + return 0; + } + + seq_printf(s, "Reg @ = 0x%p\n", bdisp->regs); + + seq_puts(s, "\nStatic:\n"); + DUMP(BLT_CTL); + DUMP(BLT_ITS); + DUMP(BLT_STA1); + DUMP(BLT_AQ1_CTL); + DUMP(BLT_AQ1_IP); + DUMP(BLT_AQ1_LNA); + DUMP(BLT_AQ1_STA); + DUMP(BLT_ITM0); + + seq_puts(s, "\nPlugs:\n"); + DUMP(BLT_PLUGS1_OP2); + DUMP(BLT_PLUGS1_CHZ); + DUMP(BLT_PLUGS1_MSZ); + DUMP(BLT_PLUGS1_PGZ); + DUMP(BLT_PLUGS2_OP2); + DUMP(BLT_PLUGS2_CHZ); + DUMP(BLT_PLUGS2_MSZ); + DUMP(BLT_PLUGS2_PGZ); + DUMP(BLT_PLUGS3_OP2); + DUMP(BLT_PLUGS3_CHZ); + DUMP(BLT_PLUGS3_MSZ); + DUMP(BLT_PLUGS3_PGZ); + DUMP(BLT_PLUGT_OP2); + DUMP(BLT_PLUGT_CHZ); + DUMP(BLT_PLUGT_MSZ); + DUMP(BLT_PLUGT_PGZ); + + seq_puts(s, "\nNode:\n"); + DUMP(BLT_NIP); + DUMP(BLT_CIC); + DUMP(BLT_INS); + DUMP(BLT_ACK); + DUMP(BLT_TBA); + DUMP(BLT_TTY); + DUMP(BLT_TXY); + DUMP(BLT_TSZ); + DUMP(BLT_S1BA); + DUMP(BLT_S1TY); + DUMP(BLT_S1XY); + DUMP(BLT_S2BA); + DUMP(BLT_S2TY); + DUMP(BLT_S2XY); + DUMP(BLT_S2SZ); + DUMP(BLT_S3BA); + DUMP(BLT_S3TY); + DUMP(BLT_S3XY); + DUMP(BLT_S3SZ); + DUMP(BLT_FCTL); + DUMP(BLT_RSF); + DUMP(BLT_RZI); + DUMP(BLT_HFP); + DUMP(BLT_VFP); + DUMP(BLT_Y_RSF); + DUMP(BLT_Y_RZI); + DUMP(BLT_Y_HFP); + DUMP(BLT_Y_VFP); + DUMP(BLT_IVMX0); + DUMP(BLT_IVMX1); + DUMP(BLT_IVMX2); + DUMP(BLT_IVMX3); + DUMP(BLT_OVMX0); + DUMP(BLT_OVMX1); + DUMP(BLT_OVMX2); + DUMP(BLT_OVMX3); + DUMP(BLT_DEI); + + seq_puts(s, "\nFilter:\n"); + for (i = 0; i < BLT_NB_H_COEF; i++) { + seq_printf(s, "BLT_HFC%d \t0x%08X\n", i, + readl(bdisp->regs + BLT_HFC_N + i * 4)); + } + for (i = 0; i < BLT_NB_V_COEF; i++) { + seq_printf(s, "BLT_VFC%d \t0x%08X\n", i, + readl(bdisp->regs + BLT_VFC_N + i * 4)); + } + + seq_puts(s, "\nLuma filter:\n"); + for (i = 0; i < BLT_NB_H_COEF; i++) { + seq_printf(s, "BLT_Y_HFC%d \t0x%08X\n", i, + readl(bdisp->regs + BLT_Y_HFC_N + i * 4)); + } + for (i = 0; i < BLT_NB_V_COEF; i++) { + seq_printf(s, "BLT_Y_VFC%d \t0x%08X\n", i, + readl(bdisp->regs + BLT_Y_VFC_N + i * 4)); + } + + pm_runtime_put(bdisp->dev); + + return 0; +} + +#define SECOND 1000000 + +static int bdisp_dbg_perf(struct seq_file *s, void *data) +{ + struct bdisp_dev *bdisp = s->private; + struct bdisp_request *request = &bdisp->dbg.copy_request; + s64 avg_time_us; + int avg_fps, min_fps, max_fps, last_fps; + + if (!request->nb_req) { + seq_puts(s, "No request\n"); + return 0; + } + + avg_time_us = div64_s64(bdisp->dbg.tot_duration, request->nb_req); + if (avg_time_us > SECOND) + avg_fps = 0; + else + avg_fps = SECOND / (s32)avg_time_us; + + if (bdisp->dbg.min_duration > SECOND) + min_fps = 0; + else + min_fps = SECOND / (s32)bdisp->dbg.min_duration; + + if (bdisp->dbg.max_duration > SECOND) + max_fps = 0; + else + max_fps = SECOND / (s32)bdisp->dbg.max_duration; + + if (bdisp->dbg.last_duration > SECOND) + last_fps = 0; + else + last_fps = SECOND / (s32)bdisp->dbg.last_duration; + + seq_printf(s, "HW processing (%d requests):\n", request->nb_req); + seq_printf(s, " Average: %5lld us (%3d fps)\n", + avg_time_us, avg_fps); + seq_printf(s, " Min-Max: %5lld us (%3d fps) - %5lld us (%3d fps)\n", + bdisp->dbg.min_duration, min_fps, + bdisp->dbg.max_duration, max_fps); + seq_printf(s, " Last: %5lld us (%3d fps)\n", + bdisp->dbg.last_duration, last_fps); + + return 0; +} + +#define bdisp_dbg_declare(name) \ + static int bdisp_dbg_##name##_open(struct inode *i, struct file *f) \ + { \ + return single_open(f, bdisp_dbg_##name, i->i_private); \ + } \ + static const struct file_operations bdisp_dbg_##name##_fops = { \ + .open = bdisp_dbg_##name##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } + +#define bdisp_dbg_create_entry(name) \ + debugfs_create_file(#name, S_IRUGO, bdisp->dbg.debugfs_entry, bdisp, \ + &bdisp_dbg_##name##_fops) + +bdisp_dbg_declare(regs); +bdisp_dbg_declare(last_nodes); +bdisp_dbg_declare(last_nodes_raw); +bdisp_dbg_declare(last_request); +bdisp_dbg_declare(perf); + +int bdisp_debugfs_create(struct bdisp_dev *bdisp) +{ + char dirname[16]; + + snprintf(dirname, sizeof(dirname), "%s%d", BDISP_NAME, bdisp->id); + bdisp->dbg.debugfs_entry = debugfs_create_dir(dirname, NULL); + if (!bdisp->dbg.debugfs_entry) + goto err; + + if (!bdisp_dbg_create_entry(regs)) + goto err; + + if (!bdisp_dbg_create_entry(last_nodes)) + goto err; + + if (!bdisp_dbg_create_entry(last_nodes_raw)) + goto err; + + if (!bdisp_dbg_create_entry(last_request)) + goto err; + + if (!bdisp_dbg_create_entry(perf)) + goto err; + + return 0; + +err: + bdisp_debugfs_remove(bdisp); + return 0; +} + +void bdisp_debugfs_remove(struct bdisp_dev *bdisp) +{ + debugfs_remove_recursive(bdisp->dbg.debugfs_entry); + bdisp->dbg.debugfs_entry = NULL; +} diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp-filter.h b/kernel/drivers/media/platform/sti/bdisp/bdisp-filter.h new file mode 100644 index 000000000..fc8c54f72 --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp-filter.h @@ -0,0 +1,346 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#define BDISP_HF_NB 64 +#define BDISP_VF_NB 40 + +/** + * struct bdisp_filter_h_spec - Horizontal filter specification + * + * @min: min scale factor for this filter (6.10 fixed point) + * @max: max scale factor for this filter (6.10 fixed point) + * coef: filter coefficients + */ +struct bdisp_filter_h_spec { + const u16 min; + const u16 max; + const u8 coef[BDISP_HF_NB]; +}; + +static const struct bdisp_filter_h_spec bdisp_h_spec[] = { + { + .min = 0, + .max = 921, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00, + 0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00, + 0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00, + 0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00, + 0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00, + 0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00, + 0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00 + } + }, + { + .min = 921, + .max = 1024, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1024, + .max = 1126, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1126, + .max = 1228, + .coef = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe, + 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc, + 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb, + 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb, + 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd, + 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff + } + }, + { + .min = 1228, + .max = 1331, + .coef = { + 0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04, + 0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02, + 0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00, + 0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff, + 0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd, + 0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc, + 0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb, + 0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc + } + }, + { + .min = 1331, + .max = 1433, + .coef = { + 0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06, + 0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05, + 0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04, + 0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02, + 0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00, + 0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff, + 0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe, + 0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd + } + }, + { + .min = 1433, + .max = 1536, + .coef = { + 0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06, + 0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06, + 0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06, + 0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04, + 0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03, + 0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01, + 0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00, + 0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff + } + }, + { + .min = 1536, + .max = 2048, + .coef = { + 0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd, + 0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff, + 0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00, + 0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01, + 0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02, + 0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03, + 0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04, + 0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05 + } + }, + { + .min = 2048, + .max = 3072, + .coef = { + 0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd, + 0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc, + 0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc, + 0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb, + 0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb, + 0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb, + 0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb, + 0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc + } + }, + { + .min = 3072, + .max = 4096, + .coef = { + 0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02, + 0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01, + 0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00, + 0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00, + 0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00, + 0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00, + 0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff, + 0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff + } + }, + { + .min = 4096, + .max = 5120, + .coef = { + 0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04, + 0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04, + 0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03, + 0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03, + 0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02, + 0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02, + 0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01, + 0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01 + } + }, + { + .min = 5120, + .max = 65535, + .coef = { + 0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06, + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, + 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05, + 0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04, + 0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04, + 0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04, + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03, + 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03 + } + } +}; + +/** + * struct bdisp_filter_v_spec - Vertical filter specification + * + * @min: min scale factor for this filter (6.10 fixed point) + * @max: max scale factor for this filter (6.10 fixed point) + * coef: filter coefficients + */ +struct bdisp_filter_v_spec { + const u16 min; + const u16 max; + const u8 coef[BDISP_VF_NB]; +}; + +static const struct bdisp_filter_v_spec bdisp_v_spec[] = { + { + .min = 0, + .max = 1024, + .coef = { + 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x06, 0x3d, 0xfd, 0x00, + 0xfe, 0x0f, 0x38, 0xfb, 0x00, + 0xfd, 0x19, 0x2f, 0xfb, 0x00, + 0xfc, 0x24, 0x24, 0xfc, 0x00, + 0xfb, 0x2f, 0x19, 0xfd, 0x00, + 0xfb, 0x38, 0x0f, 0xfe, 0x00, + 0xfd, 0x3d, 0x06, 0x00, 0x00 + } + }, + { + .min = 1024, + .max = 1331, + .coef = { + 0xfc, 0x05, 0x3e, 0x05, 0xfc, + 0xf8, 0x0e, 0x3b, 0xff, 0x00, + 0xf5, 0x18, 0x38, 0xf9, 0x02, + 0xf4, 0x21, 0x31, 0xf5, 0x05, + 0xf4, 0x2a, 0x27, 0xf4, 0x07, + 0xf6, 0x30, 0x1e, 0xf4, 0x08, + 0xf9, 0x35, 0x15, 0xf6, 0x07, + 0xff, 0x37, 0x0b, 0xf9, 0x06 + } + }, + { + .min = 1331, + .max = 1433, + .coef = { + 0xf8, 0x0a, 0x3c, 0x0a, 0xf8, + 0xf6, 0x12, 0x3b, 0x02, 0xfb, + 0xf4, 0x1b, 0x35, 0xfd, 0xff, + 0xf4, 0x23, 0x30, 0xf8, 0x01, + 0xf6, 0x29, 0x27, 0xf6, 0x04, + 0xf9, 0x2e, 0x1e, 0xf5, 0x06, + 0xfd, 0x31, 0x16, 0xf6, 0x06, + 0x02, 0x32, 0x0d, 0xf8, 0x07 + } + }, + { + .min = 1433, + .max = 1536, + .coef = { + 0xf6, 0x0e, 0x38, 0x0e, 0xf6, + 0xf5, 0x15, 0x38, 0x06, 0xf8, + 0xf5, 0x1d, 0x33, 0x00, 0xfb, + 0xf6, 0x23, 0x2d, 0xfc, 0xfe, + 0xf9, 0x28, 0x26, 0xf9, 0x00, + 0xfc, 0x2c, 0x1e, 0xf7, 0x03, + 0x00, 0x2e, 0x18, 0xf6, 0x04, + 0x05, 0x2e, 0x11, 0xf7, 0x05 + } + }, + { + .min = 1536, + .max = 2048, + .coef = { + 0xfb, 0x13, 0x24, 0x13, 0xfb, + 0xfd, 0x17, 0x23, 0x0f, 0xfa, + 0xff, 0x1a, 0x23, 0x0b, 0xf9, + 0x01, 0x1d, 0x22, 0x07, 0xf9, + 0x04, 0x20, 0x1f, 0x04, 0xf9, + 0x07, 0x22, 0x1c, 0x01, 0xfa, + 0x0b, 0x24, 0x17, 0xff, 0xfb, + 0x0f, 0x24, 0x14, 0xfd, 0xfc + } + }, + { + .min = 2048, + .max = 3072, + .coef = { + 0x05, 0x10, 0x16, 0x10, 0x05, + 0x06, 0x11, 0x16, 0x0f, 0x04, + 0x08, 0x13, 0x15, 0x0e, 0x02, + 0x09, 0x14, 0x16, 0x0c, 0x01, + 0x0b, 0x15, 0x15, 0x0b, 0x00, + 0x0d, 0x16, 0x13, 0x0a, 0x00, + 0x0f, 0x17, 0x13, 0x08, 0xff, + 0x11, 0x18, 0x12, 0x07, 0xfe + } + }, + { + .min = 3072, + .max = 4096, + .coef = { + 0x09, 0x0f, 0x10, 0x0f, 0x09, + 0x09, 0x0f, 0x12, 0x0e, 0x08, + 0x0a, 0x10, 0x11, 0x0e, 0x07, + 0x0b, 0x11, 0x11, 0x0d, 0x06, + 0x0c, 0x11, 0x12, 0x0c, 0x05, + 0x0d, 0x12, 0x11, 0x0c, 0x04, + 0x0e, 0x12, 0x11, 0x0b, 0x04, + 0x0f, 0x13, 0x11, 0x0a, 0x03 + } + }, + { + .min = 4096, + .max = 5120, + .coef = { + 0x0a, 0x0e, 0x10, 0x0e, 0x0a, + 0x0b, 0x0e, 0x0f, 0x0e, 0x0a, + 0x0b, 0x0f, 0x10, 0x0d, 0x09, + 0x0c, 0x0f, 0x10, 0x0d, 0x08, + 0x0d, 0x0f, 0x0f, 0x0d, 0x08, + 0x0d, 0x10, 0x10, 0x0c, 0x07, + 0x0e, 0x10, 0x0f, 0x0c, 0x07, + 0x0f, 0x10, 0x10, 0x0b, 0x06 + } + }, + { + .min = 5120, + .max = 65535, + .coef = { + 0x0b, 0x0e, 0x0e, 0x0e, 0x0b, + 0x0b, 0x0e, 0x0f, 0x0d, 0x0b, + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, + 0x0c, 0x0e, 0x0f, 0x0d, 0x0a, + 0x0d, 0x0f, 0x0e, 0x0d, 0x09, + 0x0d, 0x0f, 0x0f, 0x0c, 0x09, + 0x0e, 0x0f, 0x0e, 0x0c, 0x09, + 0x0e, 0x0f, 0x0f, 0x0c, 0x08 + } + } +}; + +#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec) +#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec) + +/* RGB YUV 601 standard conversion */ +static const u32 bdisp_rgb_to_yuv[] = { + 0x0e1e8bee, 0x08420419, 0xfb5ed471, 0x08004080, +}; + +static const u32 bdisp_yuv_to_rgb[] = { + 0x3324a800, 0xe604ab9c, 0x0004a957, 0x32121eeb, +}; diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp-hw.c b/kernel/drivers/media/platform/sti/bdisp/bdisp-hw.c new file mode 100644 index 000000000..052c932ac --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -0,0 +1,823 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include + +#include "bdisp.h" +#include "bdisp-filter.h" +#include "bdisp-reg.h" + +/* Max width of the source frame in a single node */ +#define MAX_SRC_WIDTH 2048 + +/* Reset & boot poll config */ +#define POLL_RST_MAX 50 +#define POLL_RST_DELAY_MS 20 + +enum bdisp_target_plan { + BDISP_RGB, + BDISP_Y, + BDISP_CBCR +}; + +struct bdisp_op_cfg { + bool cconv; /* RGB - YUV conversion */ + bool hflip; /* Horizontal flip */ + bool vflip; /* Vertical flip */ + bool wide; /* Wide (>MAX_SRC_WIDTH) */ + bool scale; /* Scale */ + u16 h_inc; /* Horizontal increment in 6.10 format */ + u16 v_inc; /* Vertical increment in 6.10 format */ + bool src_interlaced; /* is the src an interlaced buffer */ + u8 src_nbp; /* nb of planes of the src */ + bool src_yuv; /* is the src a YUV color format */ + bool src_420; /* is the src 4:2:0 chroma subsampled */ + u8 dst_nbp; /* nb of planes of the dst */ + bool dst_yuv; /* is the dst a YUV color format */ + bool dst_420; /* is the dst 4:2:0 chroma subsampled */ +}; + +struct bdisp_filter_addr { + u16 min; /* Filter min scale factor (6.10 fixed point) */ + u16 max; /* Filter max scale factor (6.10 fixed point) */ + void *virt; /* Virtual address for filter table */ + dma_addr_t paddr; /* Physical address for filter table */ +}; + +static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER]; +static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER]; + +/** + * bdisp_hw_reset + * @bdisp: bdisp entity + * + * Resets HW + * + * RETURNS: + * 0 on success. + */ +int bdisp_hw_reset(struct bdisp_dev *bdisp) +{ + unsigned int i; + + dev_dbg(bdisp->dev, "%s\n", __func__); + + /* Mask Interrupt */ + writel(0, bdisp->regs + BLT_ITM0); + + /* Reset */ + writel(readl(bdisp->regs + BLT_CTL) | BLT_CTL_RESET, + bdisp->regs + BLT_CTL); + writel(0, bdisp->regs + BLT_CTL); + + /* Wait for reset done */ + for (i = 0; i < POLL_RST_MAX; i++) { + if (readl(bdisp->regs + BLT_STA1) & BLT_STA1_IDLE) + break; + msleep(POLL_RST_DELAY_MS); + } + if (i == POLL_RST_MAX) + dev_err(bdisp->dev, "Reset timeout\n"); + + return (i == POLL_RST_MAX) ? -EAGAIN : 0; +} + +/** + * bdisp_hw_get_and_clear_irq + * @bdisp: bdisp entity + * + * Read then reset interrupt status + * + * RETURNS: + * 0 if expected interrupt was raised. + */ +int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp) +{ + u32 its; + + its = readl(bdisp->regs + BLT_ITS); + + /* Check for the only expected IT: LastNode of AQ1 */ + if (!(its & BLT_ITS_AQ1_LNA)) { + dev_dbg(bdisp->dev, "Unexpected IT status: 0x%08X\n", its); + writel(its, bdisp->regs + BLT_ITS); + return -1; + } + + /* Clear and mask */ + writel(its, bdisp->regs + BLT_ITS); + writel(0, bdisp->regs + BLT_ITM0); + + return 0; +} + +/** + * bdisp_hw_free_nodes + * @ctx: bdisp context + * + * Free node memory + * + * RETURNS: + * None + */ +void bdisp_hw_free_nodes(struct bdisp_ctx *ctx) +{ + if (ctx && ctx->node[0]) { + DEFINE_DMA_ATTRS(attrs); + + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + dma_free_attrs(ctx->bdisp_dev->dev, + sizeof(struct bdisp_node) * MAX_NB_NODE, + ctx->node[0], ctx->node_paddr[0], &attrs); + } +} + +/** + * bdisp_hw_alloc_nodes + * @ctx: bdisp context + * + * Allocate dma memory for nodes + * + * RETURNS: + * 0 on success + */ +int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx) +{ + struct device *dev = ctx->bdisp_dev->dev; + unsigned int i, node_size = sizeof(struct bdisp_node); + void *base; + dma_addr_t paddr; + DEFINE_DMA_ATTRS(attrs); + + /* Allocate all the nodes within a single memory page */ + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr, + GFP_KERNEL | GFP_DMA, &attrs); + if (!base) { + dev_err(dev, "%s no mem\n", __func__); + return -ENOMEM; + } + + memset(base, 0, node_size * MAX_NB_NODE); + + for (i = 0; i < MAX_NB_NODE; i++) { + ctx->node[i] = base; + ctx->node_paddr[i] = paddr; + dev_dbg(dev, "node[%d]=0x%p (paddr=%pad)\n", i, ctx->node[i], + &paddr); + base += node_size; + paddr += node_size; + } + + return 0; +} + +/** + * bdisp_hw_free_filters + * @dev: device + * + * Free filters memory + * + * RETURNS: + * None + */ +void bdisp_hw_free_filters(struct device *dev) +{ + int size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER); + + if (bdisp_h_filter[0].virt) { + DEFINE_DMA_ATTRS(attrs); + + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + dma_free_attrs(dev, size, bdisp_h_filter[0].virt, + bdisp_h_filter[0].paddr, &attrs); + } +} + +/** + * bdisp_hw_alloc_filters + * @dev: device + * + * Allocate dma memory for filters + * + * RETURNS: + * 0 on success + */ +int bdisp_hw_alloc_filters(struct device *dev) +{ + unsigned int i, size; + void *base; + dma_addr_t paddr; + DEFINE_DMA_ATTRS(attrs); + + /* Allocate all the filters within a single memory page */ + size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER); + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs); + if (!base) + return -ENOMEM; + + /* Setup filter addresses */ + for (i = 0; i < NB_H_FILTER; i++) { + bdisp_h_filter[i].min = bdisp_h_spec[i].min; + bdisp_h_filter[i].max = bdisp_h_spec[i].max; + memcpy(base, bdisp_h_spec[i].coef, BDISP_HF_NB); + bdisp_h_filter[i].virt = base; + bdisp_h_filter[i].paddr = paddr; + base += BDISP_HF_NB; + paddr += BDISP_HF_NB; + } + + for (i = 0; i < NB_V_FILTER; i++) { + bdisp_v_filter[i].min = bdisp_v_spec[i].min; + bdisp_v_filter[i].max = bdisp_v_spec[i].max; + memcpy(base, bdisp_v_spec[i].coef, BDISP_VF_NB); + bdisp_v_filter[i].virt = base; + bdisp_v_filter[i].paddr = paddr; + base += BDISP_VF_NB; + paddr += BDISP_VF_NB; + } + + return 0; +} + +/** + * bdisp_hw_get_hf_addr + * @inc: resize increment + * + * Find the horizontal filter table that fits the resize increment + * + * RETURNS: + * table physical address + */ +static dma_addr_t bdisp_hw_get_hf_addr(u16 inc) +{ + unsigned int i; + + for (i = NB_H_FILTER - 1; i > 0; i--) + if ((bdisp_h_filter[i].min < inc) && + (inc <= bdisp_h_filter[i].max)) + break; + + return bdisp_h_filter[i].paddr; +} + +/** + * bdisp_hw_get_vf_addr + * @inc: resize increment + * + * Find the vertical filter table that fits the resize increment + * + * RETURNS: + * table physical address + */ +static dma_addr_t bdisp_hw_get_vf_addr(u16 inc) +{ + unsigned int i; + + for (i = NB_V_FILTER - 1; i > 0; i--) + if ((bdisp_v_filter[i].min < inc) && + (inc <= bdisp_v_filter[i].max)) + break; + + return bdisp_v_filter[i].paddr; +} + +/** + * bdisp_hw_get_inc + * @from: input size + * @to: output size + * @inc: resize increment in 6.10 format + * + * Computes the increment (inverse of scale) in 6.10 format + * + * RETURNS: + * 0 on success + */ +static int bdisp_hw_get_inc(u32 from, u32 to, u16 *inc) +{ + u32 tmp; + + if (!to) + return -EINVAL; + + if (to == from) { + *inc = 1 << 10; + return 0; + } + + tmp = (from << 10) / to; + if ((tmp > 0xFFFF) || (!tmp)) + /* overflow (downscale x 63) or too small (upscale x 1024) */ + return -EINVAL; + + *inc = (u16)tmp; + + return 0; +} + +/** + * bdisp_hw_get_hv_inc + * @ctx: device context + * @h_inc: horizontal increment + * @v_inc: vertical increment + * + * Computes the horizontal & vertical increments (inverse of scale) + * + * RETURNS: + * 0 on success + */ +static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc) +{ + u32 src_w, src_h, dst_w, dst_h; + + src_w = ctx->src.crop.width; + src_h = ctx->src.crop.height; + dst_w = ctx->dst.crop.width; + dst_h = ctx->dst.crop.height; + + if (bdisp_hw_get_inc(src_w, dst_w, h_inc) || + bdisp_hw_get_inc(src_h, dst_h, v_inc)) { + dev_err(ctx->bdisp_dev->dev, + "scale factors failed (%dx%d)->(%dx%d)\n", + src_w, src_h, dst_w, dst_h); + return -EINVAL; + } + + return 0; +} + +/** + * bdisp_hw_get_op_cfg + * @ctx: device context + * @c: operation configuration + * + * Check which blitter operations are expected and sets the scaling increments + * + * RETURNS: + * 0 on success + */ +static int bdisp_hw_get_op_cfg(struct bdisp_ctx *ctx, struct bdisp_op_cfg *c) +{ + struct device *dev = ctx->bdisp_dev->dev; + struct bdisp_frame *src = &ctx->src; + struct bdisp_frame *dst = &ctx->dst; + + if (src->width > MAX_SRC_WIDTH * MAX_VERTICAL_STRIDES) { + dev_err(dev, "Image width out of HW caps\n"); + return -EINVAL; + } + + c->wide = src->width > MAX_SRC_WIDTH; + + c->hflip = ctx->hflip; + c->vflip = ctx->vflip; + + c->src_interlaced = (src->field == V4L2_FIELD_INTERLACED); + + c->src_nbp = src->fmt->nb_planes; + c->src_yuv = (src->fmt->pixelformat == V4L2_PIX_FMT_NV12) || + (src->fmt->pixelformat == V4L2_PIX_FMT_YUV420); + c->src_420 = c->src_yuv; + + c->dst_nbp = dst->fmt->nb_planes; + c->dst_yuv = (dst->fmt->pixelformat == V4L2_PIX_FMT_NV12) || + (dst->fmt->pixelformat == V4L2_PIX_FMT_YUV420); + c->dst_420 = c->dst_yuv; + + c->cconv = (c->src_yuv != c->dst_yuv); + + if (bdisp_hw_get_hv_inc(ctx, &c->h_inc, &c->v_inc)) { + dev_err(dev, "Scale factor out of HW caps\n"); + return -EINVAL; + } + + /* Deinterlacing adjustment : stretch a field to a frame */ + if (c->src_interlaced) + c->v_inc /= 2; + + if ((c->h_inc != (1 << 10)) || (c->v_inc != (1 << 10))) + c->scale = true; + else + c->scale = false; + + return 0; +} + +/** + * bdisp_hw_color_format + * @pixelformat: v4l2 pixel format + * + * v4l2 to bdisp pixel format convert + * + * RETURNS: + * bdisp pixel format + */ +static u32 bdisp_hw_color_format(u32 pixelformat) +{ + u32 ret; + + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + ret = (BDISP_YUV_3B << BLT_TTY_COL_SHIFT); + break; + case V4L2_PIX_FMT_NV12: + ret = (BDISP_NV12 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END; + break; + case V4L2_PIX_FMT_RGB565: + ret = (BDISP_RGB565 << BLT_TTY_COL_SHIFT); + break; + case V4L2_PIX_FMT_XBGR32: /* This V4L format actually refers to xRGB */ + ret = (BDISP_XRGB8888 << BLT_TTY_COL_SHIFT); + break; + case V4L2_PIX_FMT_RGB24: /* RGB888 format */ + ret = (BDISP_RGB888 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END; + break; + case V4L2_PIX_FMT_ABGR32: /* This V4L format actually refers to ARGB */ + + default: + ret = (BDISP_ARGB8888 << BLT_TTY_COL_SHIFT) | BLT_TTY_ALPHA_R; + break; + } + + return ret; +} + +/** + * bdisp_hw_build_node + * @ctx: device context + * @cfg: operation configuration + * @node: node to be set + * @t_plan: whether the node refers to a RGB/Y or a CbCr plane + * @src_x_offset: x offset in the source image + * + * Build a node + * + * RETURNS: + * None + */ +static void bdisp_hw_build_node(struct bdisp_ctx *ctx, + struct bdisp_op_cfg *cfg, + struct bdisp_node *node, + enum bdisp_target_plan t_plan, int src_x_offset) +{ + struct bdisp_frame *src = &ctx->src; + struct bdisp_frame *dst = &ctx->dst; + u16 h_inc, v_inc, yh_inc, yv_inc; + struct v4l2_rect src_rect = src->crop; + struct v4l2_rect dst_rect = dst->crop; + int dst_x_offset; + s32 dst_width = dst->crop.width; + u32 src_fmt, dst_fmt; + const u32 *ivmx; + + dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); + + memset(node, 0, sizeof(*node)); + + /* Adjust src and dst areas wrt src_x_offset */ + src_rect.left += src_x_offset; + src_rect.width -= src_x_offset; + src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width); + + dst_x_offset = (src_x_offset * dst_width) / ctx->src.crop.width; + dst_rect.left += dst_x_offset; + dst_rect.width = (src_rect.width * dst_width) / ctx->src.crop.width; + + /* General */ + src_fmt = src->fmt->pixelformat; + dst_fmt = dst->fmt->pixelformat; + + node->nip = 0; + node->cic = BLT_CIC_ALL_GRP; + node->ack = BLT_ACK_BYPASS_S2S3; + + switch (cfg->src_nbp) { + case 1: + /* Src2 = RGB / Src1 = Src3 = off */ + node->ins = BLT_INS_S1_OFF | BLT_INS_S2_MEM | BLT_INS_S3_OFF; + break; + case 2: + /* Src3 = Y + * Src2 = CbCr or ColorFill if writing the Y plane + * Src1 = off */ + node->ins = BLT_INS_S1_OFF | BLT_INS_S3_MEM; + if (t_plan == BDISP_Y) + node->ins |= BLT_INS_S2_CF; + else + node->ins |= BLT_INS_S2_MEM; + break; + case 3: + default: + /* Src3 = Y + * Src2 = Cb or ColorFill if writing the Y plane + * Src1 = Cr or ColorFill if writing the Y plane */ + node->ins = BLT_INS_S3_MEM; + if (t_plan == BDISP_Y) + node->ins |= BLT_INS_S2_CF | BLT_INS_S1_CF; + else + node->ins |= BLT_INS_S2_MEM | BLT_INS_S1_MEM; + break; + } + + /* Color convert */ + node->ins |= cfg->cconv ? BLT_INS_IVMX : 0; + /* Scale needed if scaling OR 4:2:0 up/downsampling */ + node->ins |= (cfg->scale || cfg->src_420 || cfg->dst_420) ? + BLT_INS_SCALE : 0; + + /* Target */ + node->tba = (t_plan == BDISP_CBCR) ? dst->paddr[1] : dst->paddr[0]; + + node->tty = dst->bytesperline; + node->tty |= bdisp_hw_color_format(dst_fmt); + node->tty |= BLT_TTY_DITHER; + node->tty |= (t_plan == BDISP_CBCR) ? BLT_TTY_CHROMA : 0; + node->tty |= cfg->hflip ? BLT_TTY_HSO : 0; + node->tty |= cfg->vflip ? BLT_TTY_VSO : 0; + + if (cfg->dst_420 && (t_plan == BDISP_CBCR)) { + /* 420 chroma downsampling */ + dst_rect.height /= 2; + dst_rect.width /= 2; + dst_rect.left /= 2; + dst_rect.top /= 2; + dst_x_offset /= 2; + dst_width /= 2; + } + + node->txy = cfg->vflip ? (dst_rect.height - 1) : dst_rect.top; + node->txy <<= 16; + node->txy |= cfg->hflip ? (dst_width - dst_x_offset - 1) : + dst_rect.left; + + node->tsz = dst_rect.height << 16 | dst_rect.width; + + if (cfg->src_interlaced) { + /* handle only the top field which is half height of a frame */ + src_rect.top /= 2; + src_rect.height /= 2; + } + + if (cfg->src_nbp == 1) { + /* Src 2 : RGB */ + node->s2ba = src->paddr[0]; + + node->s2ty = src->bytesperline; + if (cfg->src_interlaced) + node->s2ty *= 2; + + node->s2ty |= bdisp_hw_color_format(src_fmt); + + node->s2xy = src_rect.top << 16 | src_rect.left; + node->s2sz = src_rect.height << 16 | src_rect.width; + } else { + /* Src 2 : Cb or CbCr */ + if (cfg->src_420) { + /* 420 chroma upsampling */ + src_rect.top /= 2; + src_rect.left /= 2; + src_rect.width /= 2; + src_rect.height /= 2; + } + + node->s2ba = src->paddr[1]; + + node->s2ty = src->bytesperline; + if (cfg->src_nbp == 3) + node->s2ty /= 2; + if (cfg->src_interlaced) + node->s2ty *= 2; + + node->s2ty |= bdisp_hw_color_format(src_fmt); + + node->s2xy = src_rect.top << 16 | src_rect.left; + node->s2sz = src_rect.height << 16 | src_rect.width; + + if (cfg->src_nbp == 3) { + /* Src 1 : Cr */ + node->s1ba = src->paddr[2]; + + node->s1ty = node->s2ty; + node->s1xy = node->s2xy; + } + + /* Src 3 : Y */ + node->s3ba = src->paddr[0]; + + node->s3ty = src->bytesperline; + if (cfg->src_interlaced) + node->s3ty *= 2; + node->s3ty |= bdisp_hw_color_format(src_fmt); + + if ((t_plan != BDISP_CBCR) && cfg->src_420) { + /* No chroma upsampling for output RGB / Y plane */ + node->s3xy = node->s2xy * 2; + node->s3sz = node->s2sz * 2; + } else { + /* No need to read Y (Src3) when writing Chroma */ + node->s3ty |= BLT_S3TY_BLANK_ACC; + node->s3xy = node->s2xy; + node->s3sz = node->s2sz; + } + } + + /* Resize (scale OR 4:2:0: chroma up/downsampling) */ + if (node->ins & BLT_INS_SCALE) { + /* no need to compute Y when writing CbCr from RGB input */ + bool skip_y = (t_plan == BDISP_CBCR) && !cfg->src_yuv; + + /* FCTL */ + if (cfg->scale) { + node->fctl = BLT_FCTL_HV_SCALE; + if (!skip_y) + node->fctl |= BLT_FCTL_Y_HV_SCALE; + } else { + node->fctl = BLT_FCTL_HV_SAMPLE; + if (!skip_y) + node->fctl |= BLT_FCTL_Y_HV_SAMPLE; + } + + /* RSF - Chroma may need to be up/downsampled */ + h_inc = cfg->h_inc; + v_inc = cfg->v_inc; + if (!cfg->src_420 && cfg->dst_420 && (t_plan == BDISP_CBCR)) { + /* RGB to 4:2:0 for Chroma: downsample */ + h_inc *= 2; + v_inc *= 2; + } else if (cfg->src_420 && !cfg->dst_420) { + /* 4:2:0: to RGB: upsample*/ + h_inc /= 2; + v_inc /= 2; + } + node->rsf = v_inc << 16 | h_inc; + + /* RZI */ + node->rzi = BLT_RZI_DEFAULT; + + /* Filter table physical addr */ + node->hfp = bdisp_hw_get_hf_addr(h_inc); + node->vfp = bdisp_hw_get_vf_addr(v_inc); + + /* Y version */ + if (!skip_y) { + yh_inc = cfg->h_inc; + yv_inc = cfg->v_inc; + + node->y_rsf = yv_inc << 16 | yh_inc; + node->y_rzi = BLT_RZI_DEFAULT; + node->y_hfp = bdisp_hw_get_hf_addr(yh_inc); + node->y_vfp = bdisp_hw_get_vf_addr(yv_inc); + } + } + + /* Versatile matrix for RGB / YUV conversion */ + if (cfg->cconv) { + ivmx = cfg->src_yuv ? bdisp_yuv_to_rgb : bdisp_rgb_to_yuv; + + node->ivmx0 = ivmx[0]; + node->ivmx1 = ivmx[1]; + node->ivmx2 = ivmx[2]; + node->ivmx3 = ivmx[3]; + } +} + +/** + * bdisp_hw_build_all_nodes + * @ctx: device context + * + * Build all the nodes for the blitter operation + * + * RETURNS: + * 0 on success + */ +static int bdisp_hw_build_all_nodes(struct bdisp_ctx *ctx) +{ + struct bdisp_op_cfg cfg; + unsigned int i, nid = 0; + int src_x_offset = 0; + + for (i = 0; i < MAX_NB_NODE; i++) + if (!ctx->node[i]) { + dev_err(ctx->bdisp_dev->dev, "node %d is null\n", i); + return -EINVAL; + } + + /* Get configuration (scale, flip, ...) */ + if (bdisp_hw_get_op_cfg(ctx, &cfg)) + return -EINVAL; + + /* Split source in vertical strides (HW constraint) */ + for (i = 0; i < MAX_VERTICAL_STRIDES; i++) { + /* Build RGB/Y node and link it to the previous node */ + bdisp_hw_build_node(ctx, &cfg, ctx->node[nid], + cfg.dst_nbp == 1 ? BDISP_RGB : BDISP_Y, + src_x_offset); + if (nid) + ctx->node[nid - 1]->nip = ctx->node_paddr[nid]; + nid++; + + /* Build additional Cb(Cr) node, link it to the previous one */ + if (cfg.dst_nbp > 1) { + bdisp_hw_build_node(ctx, &cfg, ctx->node[nid], + BDISP_CBCR, src_x_offset); + ctx->node[nid - 1]->nip = ctx->node_paddr[nid]; + nid++; + } + + /* Next stride until full width covered */ + src_x_offset += MAX_SRC_WIDTH; + if (src_x_offset >= ctx->src.crop.width) + break; + } + + /* Mark last node as the last */ + ctx->node[nid - 1]->nip = 0; + + return 0; +} + +/** + * bdisp_hw_save_request + * @ctx: device context + * + * Save a copy of the request and of the built nodes + * + * RETURNS: + * None + */ +static void bdisp_hw_save_request(struct bdisp_ctx *ctx) +{ + struct bdisp_node **copy_node = ctx->bdisp_dev->dbg.copy_node; + struct bdisp_request *request = &ctx->bdisp_dev->dbg.copy_request; + struct bdisp_node **node = ctx->node; + int i; + + /* Request copy */ + request->src = ctx->src; + request->dst = ctx->dst; + request->hflip = ctx->hflip; + request->vflip = ctx->vflip; + request->nb_req++; + + /* Nodes copy */ + for (i = 0; i < MAX_NB_NODE; i++) { + /* Allocate memory if not done yet */ + if (!copy_node[i]) { + copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev, + sizeof(*copy_node[i]), + GFP_KERNEL); + if (!copy_node[i]) + return; + } + *copy_node[i] = *node[i]; + } +} + +/** + * bdisp_hw_update + * @ctx: device context + * + * Send the request to the HW + * + * RETURNS: + * 0 on success + */ +int bdisp_hw_update(struct bdisp_ctx *ctx) +{ + int ret; + struct bdisp_dev *bdisp = ctx->bdisp_dev; + struct device *dev = bdisp->dev; + unsigned int node_id; + + dev_dbg(dev, "%s\n", __func__); + + /* build nodes */ + ret = bdisp_hw_build_all_nodes(ctx); + if (ret) { + dev_err(dev, "cannot build nodes (%d)\n", ret); + return ret; + } + + /* Save a copy of the request */ + bdisp_hw_save_request(ctx); + + /* Configure interrupt to 'Last Node Reached for AQ1' */ + writel(BLT_AQ1_CTL_CFG, bdisp->regs + BLT_AQ1_CTL); + writel(BLT_ITS_AQ1_LNA, bdisp->regs + BLT_ITM0); + + /* Write first node addr */ + writel(ctx->node_paddr[0], bdisp->regs + BLT_AQ1_IP); + + /* Find and write last node addr : this starts the HW processing */ + for (node_id = 0; node_id < MAX_NB_NODE - 1; node_id++) { + if (!ctx->node[node_id]->nip) + break; + } + writel(ctx->node_paddr[node_id], bdisp->regs + BLT_AQ1_LNA); + + return 0; +} diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp-reg.h b/kernel/drivers/media/platform/sti/bdisp/bdisp-reg.h new file mode 100644 index 000000000..e7e1a425f --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp-reg.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +struct bdisp_node { + /* 0 - General */ + u32 nip; + u32 cic; + u32 ins; + u32 ack; + /* 1 - Target */ + u32 tba; + u32 tty; + u32 txy; + u32 tsz; + /* 2 - Color Fill */ + u32 s1cf; + u32 s2cf; + /* 3 - Source 1 */ + u32 s1ba; + u32 s1ty; + u32 s1xy; + u32 s1sz_tsz; + /* 4 - Source 2 */ + u32 s2ba; + u32 s2ty; + u32 s2xy; + u32 s2sz; + /* 5 - Source 3 */ + u32 s3ba; + u32 s3ty; + u32 s3xy; + u32 s3sz; + /* 6 - Clipping */ + u32 cwo; + u32 cws; + /* 7 - CLUT */ + u32 cco; + u32 cml; + /* 8 - Filter & Mask */ + u32 fctl; + u32 pmk; + /* 9 - Chroma Filter */ + u32 rsf; + u32 rzi; + u32 hfp; + u32 vfp; + /* 10 - Luma Filter */ + u32 y_rsf; + u32 y_rzi; + u32 y_hfp; + u32 y_vfp; + /* 11 - Flicker */ + u32 ff0; + u32 ff1; + u32 ff2; + u32 ff3; + /* 12 - Color Key */ + u32 key1; + u32 key2; + /* 14 - Static Address & User */ + u32 sar; + u32 usr; + /* 15 - Input Versatile Matrix */ + u32 ivmx0; + u32 ivmx1; + u32 ivmx2; + u32 ivmx3; + /* 16 - Output Versatile Matrix */ + u32 ovmx0; + u32 ovmx1; + u32 ovmx2; + u32 ovmx3; + /* 17 - Pace */ + u32 pace; + /* 18 - VC1R & DEI */ + u32 vc1r; + u32 dei; + /* 19 - Gradient Fill */ + u32 hgf; + u32 vgf; +}; + +/* HW registers : static */ +#define BLT_CTL 0x0A00 +#define BLT_ITS 0x0A04 +#define BLT_STA1 0x0A08 +#define BLT_AQ1_CTL 0x0A60 +#define BLT_AQ1_IP 0x0A64 +#define BLT_AQ1_LNA 0x0A68 +#define BLT_AQ1_STA 0x0A6C +#define BLT_ITM0 0x0AD0 +/* HW registers : plugs */ +#define BLT_PLUGS1_OP2 0x0B04 +#define BLT_PLUGS1_CHZ 0x0B08 +#define BLT_PLUGS1_MSZ 0x0B0C +#define BLT_PLUGS1_PGZ 0x0B10 +#define BLT_PLUGS2_OP2 0x0B24 +#define BLT_PLUGS2_CHZ 0x0B28 +#define BLT_PLUGS2_MSZ 0x0B2C +#define BLT_PLUGS2_PGZ 0x0B30 +#define BLT_PLUGS3_OP2 0x0B44 +#define BLT_PLUGS3_CHZ 0x0B48 +#define BLT_PLUGS3_MSZ 0x0B4C +#define BLT_PLUGS3_PGZ 0x0B50 +#define BLT_PLUGT_OP2 0x0B84 +#define BLT_PLUGT_CHZ 0x0B88 +#define BLT_PLUGT_MSZ 0x0B8C +#define BLT_PLUGT_PGZ 0x0B90 +/* HW registers : node */ +#define BLT_NIP 0x0C00 +#define BLT_CIC 0x0C04 +#define BLT_INS 0x0C08 +#define BLT_ACK 0x0C0C +#define BLT_TBA 0x0C10 +#define BLT_TTY 0x0C14 +#define BLT_TXY 0x0C18 +#define BLT_TSZ 0x0C1C +#define BLT_S1BA 0x0C28 +#define BLT_S1TY 0x0C2C +#define BLT_S1XY 0x0C30 +#define BLT_S2BA 0x0C38 +#define BLT_S2TY 0x0C3C +#define BLT_S2XY 0x0C40 +#define BLT_S2SZ 0x0C44 +#define BLT_S3BA 0x0C48 +#define BLT_S3TY 0x0C4C +#define BLT_S3XY 0x0C50 +#define BLT_S3SZ 0x0C54 +#define BLT_FCTL 0x0C68 +#define BLT_RSF 0x0C70 +#define BLT_RZI 0x0C74 +#define BLT_HFP 0x0C78 +#define BLT_VFP 0x0C7C +#define BLT_Y_RSF 0x0C80 +#define BLT_Y_RZI 0x0C84 +#define BLT_Y_HFP 0x0C88 +#define BLT_Y_VFP 0x0C8C +#define BLT_IVMX0 0x0CC0 +#define BLT_IVMX1 0x0CC4 +#define BLT_IVMX2 0x0CC8 +#define BLT_IVMX3 0x0CCC +#define BLT_OVMX0 0x0CD0 +#define BLT_OVMX1 0x0CD4 +#define BLT_OVMX2 0x0CD8 +#define BLT_OVMX3 0x0CDC +#define BLT_DEI 0x0CEC +/* HW registers : filters */ +#define BLT_HFC_N 0x0D00 +#define BLT_VFC_N 0x0D90 +#define BLT_Y_HFC_N 0x0E00 +#define BLT_Y_VFC_N 0x0E90 +#define BLT_NB_H_COEF 16 +#define BLT_NB_V_COEF 10 + +/* Registers values */ +#define BLT_CTL_RESET BIT(31) /* Global soft reset */ + +#define BLT_ITS_AQ1_LNA BIT(12) /* AQ1 LNA reached */ + +#define BLT_STA1_IDLE BIT(0) /* BDISP idle */ + +#define BLT_AQ1_CTL_CFG 0x80400003 /* Enable, P3, LNA reached */ + +#define BLT_INS_S1_MASK (BIT(0) | BIT(1) | BIT(2)) +#define BLT_INS_S1_OFF 0x00000000 /* src1 disabled */ +#define BLT_INS_S1_MEM 0x00000001 /* src1 fetched from memory */ +#define BLT_INS_S1_CF 0x00000003 /* src1 color fill */ +#define BLT_INS_S1_COPY 0x00000004 /* src1 direct copy */ +#define BLT_INS_S1_FILL 0x00000007 /* src1 firect fill */ +#define BLT_INS_S2_MASK (BIT(3) | BIT(4)) +#define BLT_INS_S2_OFF 0x00000000 /* src2 disabled */ +#define BLT_INS_S2_MEM 0x00000008 /* src2 fetched from memory */ +#define BLT_INS_S2_CF 0x00000018 /* src2 color fill */ +#define BLT_INS_S3_MASK BIT(5) +#define BLT_INS_S3_OFF 0x00000000 /* src3 disabled */ +#define BLT_INS_S3_MEM 0x00000020 /* src3 fetched from memory */ +#define BLT_INS_IVMX BIT(6) /* Input versatile matrix */ +#define BLT_INS_CLUT BIT(7) /* Color Look Up Table */ +#define BLT_INS_SCALE BIT(8) /* Scaling */ +#define BLT_INS_FLICK BIT(9) /* Flicker filter */ +#define BLT_INS_CLIP BIT(10) /* Clipping */ +#define BLT_INS_CKEY BIT(11) /* Color key */ +#define BLT_INS_OVMX BIT(12) /* Output versatile matrix */ +#define BLT_INS_DEI BIT(13) /* Deinterlace */ +#define BLT_INS_PMASK BIT(14) /* Plane mask */ +#define BLT_INS_VC1R BIT(17) /* VC1 Range mapping */ +#define BLT_INS_ROTATE BIT(18) /* Rotation */ +#define BLT_INS_GRAD BIT(19) /* Gradient fill */ +#define BLT_INS_AQLOCK BIT(29) /* AQ lock */ +#define BLT_INS_PACE BIT(30) /* Pace down */ +#define BLT_INS_IRQ BIT(31) /* Raise IRQ when node done */ +#define BLT_CIC_ALL_GRP 0x000FDFFC /* all valid groups present */ +#define BLT_ACK_BYPASS_S2S3 0x00000007 /* Bypass src2 and src3 */ + +#define BLT_TTY_COL_SHIFT 16 /* Color format */ +#define BLT_TTY_COL_MASK 0x001F0000 /* Color format mask */ +#define BLT_TTY_ALPHA_R BIT(21) /* Alpha range */ +#define BLT_TTY_CR_NOT_CB BIT(22) /* CR not Cb */ +#define BLT_TTY_MB BIT(23) /* MB frame / field*/ +#define BLT_TTY_HSO BIT(24) /* H scan order */ +#define BLT_TTY_VSO BIT(25) /* V scan order */ +#define BLT_TTY_DITHER BIT(26) /* Dithering */ +#define BLT_TTY_CHROMA BIT(27) /* Write chroma / luma */ +#define BLT_TTY_BIG_END BIT(30) /* Big endianness */ + +#define BLT_S1TY_A1_SUBSET BIT(22) /* A1 subset */ +#define BLT_S1TY_CHROMA_EXT BIT(26) /* Chroma Extended */ +#define BTL_S1TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */ +#define BLT_S1TY_RGB_EXP BIT(29) /* RGB expansion mode */ + +#define BLT_S2TY_A1_SUBSET BIT(22) /* A1 subset */ +#define BLT_S2TY_CHROMA_EXT BIT(26) /* Chroma Extended */ +#define BTL_S2TY_SUBBYTE BIT(28) /* Sub-byte fmt, pixel order */ +#define BLT_S2TY_RGB_EXP BIT(29) /* RGB expansion mode */ + +#define BLT_S3TY_BLANK_ACC BIT(26) /* Blank access */ + +#define BLT_FCTL_HV_SCALE 0x00000055 /* H/V resize + color filter */ +#define BLT_FCTL_Y_HV_SCALE 0x33000000 /* Luma version */ + +#define BLT_FCTL_HV_SAMPLE 0x00000044 /* H/V resize */ +#define BLT_FCTL_Y_HV_SAMPLE 0x22000000 /* Luma version */ + +#define BLT_RZI_DEFAULT 0x20003000 /* H/VNB_repeat = 3/2 */ + +/* Color format */ +#define BDISP_RGB565 0x00 /* RGB565 */ +#define BDISP_RGB888 0x01 /* RGB888 */ +#define BDISP_XRGB8888 0x02 /* RGB888_32 */ +#define BDISP_ARGB8888 0x05 /* ARGB888 */ +#define BDISP_NV12 0x16 /* YCbCr42x R2B */ +#define BDISP_YUV_3B 0x1E /* YUV (3 buffer) */ diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/kernel/drivers/media/platform/sti/bdisp/bdisp-v4l2.c new file mode 100644 index 000000000..a0d267e01 --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -0,0 +1,1444 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bdisp.h" + +#define BDISP_MAX_CTRL_NUM 10 + +#define BDISP_WORK_TIMEOUT ((100 * HZ) / 1000) + +/* User configuration change */ +#define BDISP_PARAMS BIT(0) /* Config updated */ +#define BDISP_SRC_FMT BIT(1) /* Source set */ +#define BDISP_DST_FMT BIT(2) /* Destination set */ +#define BDISP_CTX_STOP_REQ BIT(3) /* Stop request */ +#define BDISP_CTX_ABORT BIT(4) /* Abort while device run */ + +#define BDISP_MIN_W 1 +#define BDISP_MAX_W 8191 +#define BDISP_MIN_H 1 +#define BDISP_MAX_H 8191 + +#define fh_to_ctx(__fh) container_of(__fh, struct bdisp_ctx, fh) + +enum bdisp_dev_flags { + ST_M2M_OPEN, /* Driver opened */ + ST_M2M_RUNNING, /* HW device running */ + ST_M2M_SUSPENDED, /* Driver suspended */ + ST_M2M_SUSPENDING, /* Driver being suspended */ +}; + +static const struct bdisp_fmt bdisp_formats[] = { + /* ARGB888. [31:0] A:R:G:B 8:8:8:8 little endian */ + { + .pixelformat = V4L2_PIX_FMT_ABGR32, /* is actually ARGB */ + .nb_planes = 1, + .bpp = 32, + .bpp_plane0 = 32, + .w_align = 1, + .h_align = 1 + }, + /* XRGB888. [31:0] x:R:G:B 8:8:8:8 little endian */ + { + .pixelformat = V4L2_PIX_FMT_XBGR32, /* is actually xRGB */ + .nb_planes = 1, + .bpp = 32, + .bpp_plane0 = 32, + .w_align = 1, + .h_align = 1 + }, + /* RGB565. [15:0] R:G:B 5:6:5 little endian */ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .nb_planes = 1, + .bpp = 16, + .bpp_plane0 = 16, + .w_align = 1, + .h_align = 1 + }, + /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */ + { + .pixelformat = V4L2_PIX_FMT_NV12, + .nb_planes = 2, + .bpp = 12, + .bpp_plane0 = 8, + .w_align = 2, + .h_align = 2 + }, + /* RGB888. [23:0] B:G:R 8:8:8 little endian */ + { + .pixelformat = V4L2_PIX_FMT_RGB24, + .nb_planes = 1, + .bpp = 24, + .bpp_plane0 = 24, + .w_align = 1, + .h_align = 1 + }, + /* YU12. YUV420P - 1 plane for Y + 1 plane for Cb + 1 plane for Cr + * To keep as the LAST element of this table (no support on capture) + */ + { + .pixelformat = V4L2_PIX_FMT_YUV420, + .nb_planes = 3, + .bpp = 12, + .bpp_plane0 = 8, + .w_align = 2, + .h_align = 2 + } +}; + +/* Default format : HD ARGB32*/ +#define BDISP_DEF_WIDTH 1920 +#define BDISP_DEF_HEIGHT 1080 + +static const struct bdisp_frame bdisp_dflt_fmt = { + .width = BDISP_DEF_WIDTH, + .height = BDISP_DEF_HEIGHT, + .fmt = &bdisp_formats[0], + .field = V4L2_FIELD_NONE, + .bytesperline = BDISP_DEF_WIDTH * 4, + .sizeimage = BDISP_DEF_WIDTH * BDISP_DEF_HEIGHT * 4, + .colorspace = V4L2_COLORSPACE_REC709, + .crop = {0, 0, BDISP_DEF_WIDTH, BDISP_DEF_HEIGHT}, + .paddr = {0, 0, 0, 0} +}; + +static inline void bdisp_ctx_state_lock_set(u32 state, struct bdisp_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); + ctx->state |= state; + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); +} + +static inline void bdisp_ctx_state_lock_clear(u32 state, struct bdisp_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); + ctx->state &= ~state; + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); +} + +static inline bool bdisp_ctx_state_is_set(u32 mask, struct bdisp_ctx *ctx) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); + ret = (ctx->state & mask) == mask; + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); + + return ret; +} + +static const struct bdisp_fmt *bdisp_find_fmt(u32 pixelformat) +{ + const struct bdisp_fmt *fmt; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bdisp_formats); i++) { + fmt = &bdisp_formats[i]; + if (fmt->pixelformat == pixelformat) + return fmt; + } + + return NULL; +} + +static struct bdisp_frame *ctx_get_frame(struct bdisp_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &ctx->src; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &ctx->dst; + default: + dev_err(ctx->bdisp_dev->dev, + "Wrong buffer/video queue type (%d)\n", type); + break; + } + + return ERR_PTR(-EINVAL); +} + +static void bdisp_job_finish(struct bdisp_ctx *ctx, int vb_state) +{ + struct vb2_v4l2_buffer *src_vb, *dst_vb; + + if (WARN(!ctx || !ctx->fh.m2m_ctx, "Null hardware context\n")) + return; + + dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); + + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (src_vb && dst_vb) { + dst_vb->timestamp = src_vb->timestamp; + dst_vb->timecode = src_vb->timecode; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= src_vb->flags & + V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + v4l2_m2m_buf_done(src_vb, vb_state); + v4l2_m2m_buf_done(dst_vb, vb_state); + + v4l2_m2m_job_finish(ctx->bdisp_dev->m2m.m2m_dev, + ctx->fh.m2m_ctx); + } +} + +static int bdisp_ctx_stop_req(struct bdisp_ctx *ctx) +{ + struct bdisp_ctx *curr_ctx; + struct bdisp_dev *bdisp = ctx->bdisp_dev; + int ret; + + dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); + + cancel_delayed_work(&bdisp->timeout_work); + + curr_ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); + if (!test_bit(ST_M2M_RUNNING, &bdisp->state) || (curr_ctx != ctx)) + return 0; + + bdisp_ctx_state_lock_set(BDISP_CTX_STOP_REQ, ctx); + + ret = wait_event_timeout(bdisp->irq_queue, + !bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx), + BDISP_WORK_TIMEOUT); + + if (!ret) { + dev_err(ctx->bdisp_dev->dev, "%s IRQ timeout\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static void __bdisp_job_abort(struct bdisp_ctx *ctx) +{ + int ret; + + ret = bdisp_ctx_stop_req(ctx); + if ((ret == -ETIMEDOUT) || (ctx->state & BDISP_CTX_ABORT)) { + bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ | BDISP_CTX_ABORT, + ctx); + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); + } +} + +static void bdisp_job_abort(void *priv) +{ + __bdisp_job_abort((struct bdisp_ctx *)priv); +} + +static int bdisp_get_addr(struct bdisp_ctx *ctx, struct vb2_buffer *vb, + struct bdisp_frame *frame, dma_addr_t *paddr) +{ + if (!vb || !frame) + return -EINVAL; + + paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (frame->fmt->nb_planes > 1) + /* UV (NV12) or U (420P) */ + paddr[1] = (dma_addr_t)(paddr[0] + + frame->bytesperline * frame->height); + + if (frame->fmt->nb_planes > 2) + /* V (420P) */ + paddr[2] = (dma_addr_t)(paddr[1] + + (frame->bytesperline * frame->height) / 4); + + if (frame->fmt->nb_planes > 3) + dev_dbg(ctx->bdisp_dev->dev, "ignoring some planes\n"); + + dev_dbg(ctx->bdisp_dev->dev, + "%s plane[0]=%pad plane[1]=%pad plane[2]=%pad\n", + __func__, &paddr[0], &paddr[1], &paddr[2]); + + return 0; +} + +static int bdisp_get_bufs(struct bdisp_ctx *ctx) +{ + struct bdisp_frame *src, *dst; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + int ret; + + src = &ctx->src; + dst = &ctx->dst; + + src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + ret = bdisp_get_addr(ctx, &src_vb->vb2_buf, src, src->paddr); + if (ret) + return ret; + + dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + ret = bdisp_get_addr(ctx, &dst_vb->vb2_buf, dst, dst->paddr); + if (ret) + return ret; + + dst_vb->timestamp = src_vb->timestamp; + + return 0; +} + +static void bdisp_device_run(void *priv) +{ + struct bdisp_ctx *ctx = priv; + struct bdisp_dev *bdisp; + unsigned long flags; + int err = 0; + + if (WARN(!ctx, "Null hardware context\n")) + return; + + bdisp = ctx->bdisp_dev; + dev_dbg(bdisp->dev, "%s\n", __func__); + spin_lock_irqsave(&bdisp->slock, flags); + + if (bdisp->m2m.ctx != ctx) { + dev_dbg(bdisp->dev, "ctx updated: %p -> %p\n", + bdisp->m2m.ctx, ctx); + ctx->state |= BDISP_PARAMS; + bdisp->m2m.ctx = ctx; + } + + if (ctx->state & BDISP_CTX_STOP_REQ) { + ctx->state &= ~BDISP_CTX_STOP_REQ; + ctx->state |= BDISP_CTX_ABORT; + wake_up(&bdisp->irq_queue); + goto out; + } + + err = bdisp_get_bufs(ctx); + if (err) { + dev_err(bdisp->dev, "cannot get address\n"); + goto out; + } + + bdisp_dbg_perf_begin(bdisp); + + err = bdisp_hw_reset(bdisp); + if (err) { + dev_err(bdisp->dev, "could not get HW ready\n"); + goto out; + } + + err = bdisp_hw_update(ctx); + if (err) { + dev_err(bdisp->dev, "could not send HW request\n"); + goto out; + } + + queue_delayed_work(bdisp->work_queue, &bdisp->timeout_work, + BDISP_WORK_TIMEOUT); + set_bit(ST_M2M_RUNNING, &bdisp->state); +out: + ctx->state &= ~BDISP_PARAMS; + spin_unlock_irqrestore(&bdisp->slock, flags); + if (err) + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static struct v4l2_m2m_ops bdisp_m2m_ops = { + .device_run = bdisp_device_run, + .job_abort = bdisp_job_abort, +}; + +static int __bdisp_s_ctrl(struct bdisp_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctx->hflip = ctrl->val; + break; + case V4L2_CID_VFLIP: + ctx->vflip = ctrl->val; + break; + default: + dev_err(ctx->bdisp_dev->dev, "unknown control %d\n", ctrl->id); + return -EINVAL; + } + + ctx->state |= BDISP_PARAMS; + + return 0; +} + +static int bdisp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct bdisp_ctx *ctx = container_of(ctrl->handler, struct bdisp_ctx, + ctrl_handler); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); + ret = __bdisp_s_ctrl(ctx, ctrl); + spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); + + return ret; +} + +static const struct v4l2_ctrl_ops bdisp_c_ops = { + .s_ctrl = bdisp_s_ctrl, +}; + +static int bdisp_ctrls_create(struct bdisp_ctx *ctx) +{ + if (ctx->ctrls_rdy) + return 0; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, BDISP_MAX_CTRL_NUM); + + ctx->bdisp_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, + &bdisp_c_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctx->bdisp_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, + &bdisp_c_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; + } + + ctx->ctrls_rdy = true; + + return 0; +} + +static void bdisp_ctrls_delete(struct bdisp_ctx *ctx) +{ + if (ctx->ctrls_rdy) { + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + ctx->ctrls_rdy = false; + } +} + +static int bdisp_queue_setup(struct vb2_queue *vq, + const void *parg, + unsigned int *nb_buf, unsigned int *nb_planes, + unsigned int sizes[], void *allocators[]) +{ + const struct v4l2_format *fmt = parg; + struct bdisp_ctx *ctx = vb2_get_drv_priv(vq); + struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type); + + if (IS_ERR(frame)) { + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); + return PTR_ERR(frame); + } + + if (!frame->fmt) { + dev_err(ctx->bdisp_dev->dev, "Invalid format\n"); + return -EINVAL; + } + + if (fmt && fmt->fmt.pix.sizeimage < frame->sizeimage) + return -EINVAL; + + *nb_planes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : frame->sizeimage; + allocators[0] = ctx->bdisp_dev->alloc_ctx; + + return 0; +} + +static int bdisp_buf_prepare(struct vb2_buffer *vb) +{ + struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct bdisp_frame *frame = ctx_get_frame(ctx, vb->vb2_queue->type); + + if (IS_ERR(frame)) { + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); + return PTR_ERR(frame); + } + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + vb2_set_plane_payload(vb, 0, frame->sizeimage); + + return 0; +} + +static void bdisp_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + /* return to V4L2 any 0-size buffer so it can be dequeued by user */ + if (!vb2_get_plane_payload(vb, 0)) { + dev_dbg(ctx->bdisp_dev->dev, "0 data buffer, skip it\n"); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + return; + } + + if (ctx->fh.m2m_ctx) + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int bdisp_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct bdisp_ctx *ctx = q->drv_priv; + struct vb2_v4l2_buffer *buf; + int ret = pm_runtime_get_sync(ctx->bdisp_dev->dev); + + if (ret < 0) { + dev_err(ctx->bdisp_dev->dev, "failed to set runtime PM\n"); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } else { + while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + } + + return ret; + } + + return 0; +} + +static void bdisp_stop_streaming(struct vb2_queue *q) +{ + struct bdisp_ctx *ctx = q->drv_priv; + + __bdisp_job_abort(ctx); + + pm_runtime_put(ctx->bdisp_dev->dev); +} + +static struct vb2_ops bdisp_qops = { + .queue_setup = bdisp_queue_setup, + .buf_prepare = bdisp_buf_prepare, + .buf_queue = bdisp_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = bdisp_stop_streaming, + .start_streaming = bdisp_start_streaming, +}; + +static int queue_init(void *priv, + struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct bdisp_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->ops = &bdisp_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->bdisp_dev->lock; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->ops = &bdisp_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->bdisp_dev->lock; + + return vb2_queue_init(dst_vq); +} + +static int bdisp_open(struct file *file) +{ + struct bdisp_dev *bdisp = video_drvdata(file); + struct bdisp_ctx *ctx = NULL; + int ret; + + if (mutex_lock_interruptible(&bdisp->lock)) + return -ERESTARTSYS; + + /* Allocate memory for both context and node */ + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto unlock; + } + ctx->bdisp_dev = bdisp; + + if (bdisp_hw_alloc_nodes(ctx)) { + dev_err(bdisp->dev, "no memory for nodes\n"); + ret = -ENOMEM; + goto mem_ctx; + } + + v4l2_fh_init(&ctx->fh, bdisp->m2m.vdev); + + ret = bdisp_ctrls_create(ctx); + if (ret) { + dev_err(bdisp->dev, "Failed to create control\n"); + goto error_fh; + } + + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + /* Default format */ + ctx->src = bdisp_dflt_fmt; + ctx->dst = bdisp_dflt_fmt; + + /* Setup the device context for mem2mem mode. */ + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(bdisp->m2m.m2m_dev, ctx, + queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + dev_err(bdisp->dev, "Failed to initialize m2m context\n"); + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto error_ctrls; + } + + bdisp->m2m.refcnt++; + set_bit(ST_M2M_OPEN, &bdisp->state); + + dev_dbg(bdisp->dev, "driver opened, ctx = 0x%p\n", ctx); + + mutex_unlock(&bdisp->lock); + + return 0; + +error_ctrls: + bdisp_ctrls_delete(ctx); +error_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + bdisp_hw_free_nodes(ctx); +mem_ctx: + kfree(ctx); +unlock: + mutex_unlock(&bdisp->lock); + + return ret; +} + +static int bdisp_release(struct file *file) +{ + struct bdisp_ctx *ctx = fh_to_ctx(file->private_data); + struct bdisp_dev *bdisp = ctx->bdisp_dev; + + dev_dbg(bdisp->dev, "%s\n", __func__); + + if (mutex_lock_interruptible(&bdisp->lock)) + return -ERESTARTSYS; + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + bdisp_ctrls_delete(ctx); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + if (--bdisp->m2m.refcnt <= 0) + clear_bit(ST_M2M_OPEN, &bdisp->state); + + bdisp_hw_free_nodes(ctx); + + kfree(ctx); + + mutex_unlock(&bdisp->lock); + + return 0; +} + +static const struct v4l2_file_operations bdisp_fops = { + .owner = THIS_MODULE, + .open = bdisp_open, + .release = bdisp_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int bdisp_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_dev *bdisp = ctx->bdisp_dev; + + strlcpy(cap->driver, bdisp->pdev->name, sizeof(cap->driver)); + strlcpy(cap->card, bdisp->pdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", + BDISP_NAME, bdisp->id); + + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + const struct bdisp_fmt *fmt; + + if (f->index >= ARRAY_SIZE(bdisp_formats)) + return -EINVAL; + + fmt = &bdisp_formats[f->index]; + + if ((fmt->pixelformat == V4L2_PIX_FMT_YUV420) && + (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); + return -EINVAL; + } + f->pixelformat = fmt->pixelformat; + + return 0; +} + +static int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct bdisp_frame *frame = ctx_get_frame(ctx, f->type); + + if (IS_ERR(frame)) { + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); + return PTR_ERR(frame); + } + + pix = &f->fmt.pix; + pix->width = frame->width; + pix->height = frame->height; + pix->pixelformat = frame->fmt->pixelformat; + pix->field = frame->field; + pix->bytesperline = frame->bytesperline; + pix->sizeimage = frame->sizeimage; + pix->colorspace = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? + frame->colorspace : bdisp_dflt_fmt.colorspace; + + return 0; +} + +static int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct v4l2_pix_format *pix = &f->fmt.pix; + const struct bdisp_fmt *format; + u32 in_w, in_h; + + format = bdisp_find_fmt(pix->pixelformat); + if (!format) { + dev_dbg(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", + pix->pixelformat); + return -EINVAL; + } + + /* YUV420P only supported for VIDEO_OUTPUT */ + if ((format->pixelformat == V4L2_PIX_FMT_YUV420) && + (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); + return -EINVAL; + } + + /* Field (interlaced only supported on OUTPUT) */ + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (pix->field != V4L2_FIELD_INTERLACED)) + pix->field = V4L2_FIELD_NONE; + + /* Adjust width & height */ + in_w = pix->width; + in_h = pix->height; + v4l_bound_align_image(&pix->width, + BDISP_MIN_W, BDISP_MAX_W, + ffs(format->w_align) - 1, + &pix->height, + BDISP_MIN_H, BDISP_MAX_H, + ffs(format->h_align) - 1, + 0); + if ((pix->width != in_w) || (pix->height != in_h)) + dev_dbg(ctx->bdisp_dev->dev, + "%s size updated: %dx%d -> %dx%d\n", __func__, + in_w, in_h, pix->width, pix->height); + + pix->bytesperline = (pix->width * format->bpp_plane0) / 8; + pix->sizeimage = (pix->width * pix->height * format->bpp) / 8; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + pix->colorspace = bdisp_dflt_fmt.colorspace; + + return 0; +} + +static int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct vb2_queue *vq; + struct bdisp_frame *frame; + struct v4l2_pix_format *pix; + int ret; + u32 state; + + ret = bdisp_try_fmt(file, fh, f); + if (ret) { + dev_err(ctx->bdisp_dev->dev, "Cannot set format\n"); + return ret; + } + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_streaming(vq)) { + dev_err(ctx->bdisp_dev->dev, "queue (%d) busy\n", f->type); + return -EBUSY; + } + + frame = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? + &ctx->src : &ctx->dst; + pix = &f->fmt.pix; + frame->fmt = bdisp_find_fmt(pix->pixelformat); + if (!frame->fmt) { + dev_err(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", + pix->pixelformat); + return -EINVAL; + } + + frame->width = pix->width; + frame->height = pix->height; + frame->bytesperline = pix->bytesperline; + frame->sizeimage = pix->sizeimage; + frame->field = pix->field; + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + frame->colorspace = pix->colorspace; + + frame->crop.width = frame->width; + frame->crop.height = frame->height; + frame->crop.left = 0; + frame->crop.top = 0; + + state = BDISP_PARAMS; + state |= (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? + BDISP_DST_FMT : BDISP_SRC_FMT; + bdisp_ctx_state_lock_set(state, ctx); + + return 0; +} + +static int bdisp_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct bdisp_frame *frame; + struct bdisp_ctx *ctx = fh_to_ctx(fh); + + frame = ctx_get_frame(ctx, s->type); + if (IS_ERR(frame)) { + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); + return PTR_ERR(frame); + } + + switch (s->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* cropped frame */ + s->r = frame->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + /* complete frame */ + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->width; + s->r.height = frame->height; + break; + default: + dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); + return -EINVAL; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_PADDED: + /* composed (cropped) frame */ + s->r = frame->crop; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* complete frame */ + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->width; + s->r.height = frame->height; + break; + default: + dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); + return -EINVAL; + } + break; + + default: + dev_err(ctx->bdisp_dev->dev, "Invalid type\n"); + return -EINVAL; + } + + return 0; +} + +static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) +{ + /* Return 1 if a is enclosed in b, or 0 otherwise. */ + + if (a->left < b->left || a->top < b->top) + return 0; + + if (a->left + a->width > b->left + b->width) + return 0; + + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int bdisp_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct bdisp_frame *frame; + struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct v4l2_rect *in, out; + bool valid = false; + + if ((s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && + (s->target == V4L2_SEL_TGT_CROP)) + valid = true; + + if ((s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->target == V4L2_SEL_TGT_COMPOSE)) + valid = true; + + if (!valid) { + dev_err(ctx->bdisp_dev->dev, "Invalid type / target\n"); + return -EINVAL; + } + + frame = ctx_get_frame(ctx, s->type); + if (IS_ERR(frame)) { + dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); + return PTR_ERR(frame); + } + + in = &s->r; + out = *in; + + /* Align and check origin */ + out.left = ALIGN(in->left, frame->fmt->w_align); + out.top = ALIGN(in->top, frame->fmt->h_align); + + if ((out.left < 0) || (out.left >= frame->width) || + (out.top < 0) || (out.top >= frame->height)) { + dev_err(ctx->bdisp_dev->dev, + "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", + out.width, out.height, out.left, out.top, + frame->width, frame->height); + return -EINVAL; + } + + /* Align and check size */ + out.width = ALIGN(in->width, frame->fmt->w_align); + out.height = ALIGN(in->height, frame->fmt->w_align); + + if (((out.left + out.width) > frame->width) || + ((out.top + out.height) > frame->height)) { + dev_err(ctx->bdisp_dev->dev, + "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", + out.width, out.height, out.left, out.top, + frame->width, frame->height); + return -EINVAL; + } + + /* Checks adjust constraints flags */ + if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out)) + return -ERANGE; + + if ((out.left != in->left) || (out.top != in->top) || + (out.width != in->width) || (out.height != in->height)) { + dev_dbg(ctx->bdisp_dev->dev, + "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n", + __func__, in->width, in->height, in->left, in->top, + out.width, out.height, out.left, out.top); + *in = out; + } + + frame->crop = out; + + bdisp_ctx_state_lock_set(BDISP_PARAMS, ctx); + + return 0; +} + +static int bdisp_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct bdisp_ctx *ctx = fh_to_ctx(fh); + + if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && + !bdisp_ctx_state_is_set(BDISP_SRC_FMT, ctx)) { + dev_err(ctx->bdisp_dev->dev, "src not defined\n"); + return -EINVAL; + } + + if ((type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + !bdisp_ctx_state_is_set(BDISP_DST_FMT, ctx)) { + dev_err(ctx->bdisp_dev->dev, "dst not defined\n"); + return -EINVAL; + } + + return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops bdisp_ioctl_ops = { + .vidioc_querycap = bdisp_querycap, + .vidioc_enum_fmt_vid_cap = bdisp_enum_fmt, + .vidioc_enum_fmt_vid_out = bdisp_enum_fmt, + .vidioc_g_fmt_vid_cap = bdisp_g_fmt, + .vidioc_g_fmt_vid_out = bdisp_g_fmt, + .vidioc_try_fmt_vid_cap = bdisp_try_fmt, + .vidioc_try_fmt_vid_out = bdisp_try_fmt, + .vidioc_s_fmt_vid_cap = bdisp_s_fmt, + .vidioc_s_fmt_vid_out = bdisp_s_fmt, + .vidioc_g_selection = bdisp_g_selection, + .vidioc_s_selection = bdisp_s_selection, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = bdisp_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int bdisp_register_device(struct bdisp_dev *bdisp) +{ + int ret; + + if (!bdisp) + return -ENODEV; + + bdisp->vdev.fops = &bdisp_fops; + bdisp->vdev.ioctl_ops = &bdisp_ioctl_ops; + bdisp->vdev.release = video_device_release_empty; + bdisp->vdev.lock = &bdisp->lock; + bdisp->vdev.vfl_dir = VFL_DIR_M2M; + bdisp->vdev.v4l2_dev = &bdisp->v4l2_dev; + snprintf(bdisp->vdev.name, sizeof(bdisp->vdev.name), "%s.%d", + BDISP_NAME, bdisp->id); + + video_set_drvdata(&bdisp->vdev, bdisp); + + bdisp->m2m.vdev = &bdisp->vdev; + bdisp->m2m.m2m_dev = v4l2_m2m_init(&bdisp_m2m_ops); + if (IS_ERR(bdisp->m2m.m2m_dev)) { + dev_err(bdisp->dev, "failed to initialize v4l2-m2m device\n"); + return PTR_ERR(bdisp->m2m.m2m_dev); + } + + ret = video_register_device(&bdisp->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(bdisp->dev, + "%s(): failed to register video device\n", __func__); + v4l2_m2m_release(bdisp->m2m.m2m_dev); + return ret; + } + + return 0; +} + +static void bdisp_unregister_device(struct bdisp_dev *bdisp) +{ + if (!bdisp) + return; + + if (bdisp->m2m.m2m_dev) + v4l2_m2m_release(bdisp->m2m.m2m_dev); + + video_unregister_device(bdisp->m2m.vdev); +} + +static irqreturn_t bdisp_irq_thread(int irq, void *priv) +{ + struct bdisp_dev *bdisp = priv; + struct bdisp_ctx *ctx; + + spin_lock(&bdisp->slock); + + bdisp_dbg_perf_end(bdisp); + + cancel_delayed_work(&bdisp->timeout_work); + + if (!test_and_clear_bit(ST_M2M_RUNNING, &bdisp->state)) + goto isr_unlock; + + if (test_and_clear_bit(ST_M2M_SUSPENDING, &bdisp->state)) { + set_bit(ST_M2M_SUSPENDED, &bdisp->state); + wake_up(&bdisp->irq_queue); + goto isr_unlock; + } + + ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); + if (!ctx || !ctx->fh.m2m_ctx) + goto isr_unlock; + + spin_unlock(&bdisp->slock); + + bdisp_job_finish(ctx, VB2_BUF_STATE_DONE); + + if (bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx)) { + bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ, ctx); + wake_up(&bdisp->irq_queue); + } + + return IRQ_HANDLED; + +isr_unlock: + spin_unlock(&bdisp->slock); + + return IRQ_HANDLED; +} + +static irqreturn_t bdisp_irq_handler(int irq, void *priv) +{ + if (bdisp_hw_get_and_clear_irq((struct bdisp_dev *)priv)) + return IRQ_NONE; + else + return IRQ_WAKE_THREAD; +} + +static void bdisp_irq_timeout(struct work_struct *ptr) +{ + struct delayed_work *twork = to_delayed_work(ptr); + struct bdisp_dev *bdisp = container_of(twork, struct bdisp_dev, + timeout_work); + struct bdisp_ctx *ctx; + + ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); + + dev_err(ctx->bdisp_dev->dev, "Device work timeout\n"); + + spin_lock(&bdisp->slock); + clear_bit(ST_M2M_RUNNING, &bdisp->state); + spin_unlock(&bdisp->slock); + + bdisp_hw_reset(bdisp); + + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&bdisp->slock, flags); + if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { + spin_unlock_irqrestore(&bdisp->slock, flags); + return 0; + } + clear_bit(ST_M2M_SUSPENDED, &bdisp->state); + set_bit(ST_M2M_SUSPENDING, &bdisp->state); + spin_unlock_irqrestore(&bdisp->slock, flags); + + timeout = wait_event_timeout(bdisp->irq_queue, + test_bit(ST_M2M_SUSPENDED, &bdisp->state), + BDISP_WORK_TIMEOUT); + + clear_bit(ST_M2M_SUSPENDING, &bdisp->state); + + if (!timeout) { + dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); + return -EAGAIN; + } + + return 0; +} + +static int bdisp_m2m_resume(struct bdisp_dev *bdisp) +{ + struct bdisp_ctx *ctx; + unsigned long flags; + + spin_lock_irqsave(&bdisp->slock, flags); + ctx = bdisp->m2m.ctx; + bdisp->m2m.ctx = NULL; + spin_unlock_irqrestore(&bdisp->slock, flags); + + if (test_and_clear_bit(ST_M2M_SUSPENDED, &bdisp->state)) + bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); + + return 0; +} + +static int bdisp_runtime_resume(struct device *dev) +{ + struct bdisp_dev *bdisp = dev_get_drvdata(dev); + int ret = clk_enable(bdisp->clock); + + if (ret) + return ret; + + return bdisp_m2m_resume(bdisp); +} + +static int bdisp_runtime_suspend(struct device *dev) +{ + struct bdisp_dev *bdisp = dev_get_drvdata(dev); + int ret = bdisp_m2m_suspend(bdisp); + + if (!ret) + clk_disable(bdisp->clock); + + return ret; +} + +static int bdisp_resume(struct device *dev) +{ + struct bdisp_dev *bdisp = dev_get_drvdata(dev); + unsigned long flags; + int opened; + + spin_lock_irqsave(&bdisp->slock, flags); + opened = test_bit(ST_M2M_OPEN, &bdisp->state); + spin_unlock_irqrestore(&bdisp->slock, flags); + + if (!opened) + return 0; + + if (!pm_runtime_suspended(dev)) + return bdisp_runtime_resume(dev); + + return 0; +} + +static int bdisp_suspend(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + return bdisp_runtime_suspend(dev); + + return 0; +} + +static const struct dev_pm_ops bdisp_pm_ops = { + .suspend = bdisp_suspend, + .resume = bdisp_resume, + .runtime_suspend = bdisp_runtime_suspend, + .runtime_resume = bdisp_runtime_resume, +}; + +static int bdisp_remove(struct platform_device *pdev) +{ + struct bdisp_dev *bdisp = platform_get_drvdata(pdev); + + bdisp_unregister_device(bdisp); + + bdisp_hw_free_filters(bdisp->dev); + + vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); + + pm_runtime_disable(&pdev->dev); + + bdisp_debugfs_remove(bdisp); + + v4l2_device_unregister(&bdisp->v4l2_dev); + + if (!IS_ERR(bdisp->clock)) + clk_unprepare(bdisp->clock); + + dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); + + return 0; +} + +static int bdisp_probe(struct platform_device *pdev) +{ + struct bdisp_dev *bdisp; + struct resource *res; + struct device *dev = &pdev->dev; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + bdisp = devm_kzalloc(dev, sizeof(struct bdisp_dev), GFP_KERNEL); + if (!bdisp) + return -ENOMEM; + + bdisp->pdev = pdev; + bdisp->dev = dev; + platform_set_drvdata(pdev, bdisp); + + if (dev->of_node) + bdisp->id = of_alias_get_id(pdev->dev.of_node, BDISP_NAME); + else + bdisp->id = pdev->id; + + init_waitqueue_head(&bdisp->irq_queue); + INIT_DELAYED_WORK(&bdisp->timeout_work, bdisp_irq_timeout); + bdisp->work_queue = create_workqueue(BDISP_NAME); + + spin_lock_init(&bdisp->slock); + mutex_init(&bdisp->lock); + + /* get resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bdisp->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(bdisp->regs)) { + dev_err(dev, "failed to get regs\n"); + return PTR_ERR(bdisp->regs); + } + + bdisp->clock = devm_clk_get(dev, BDISP_NAME); + if (IS_ERR(bdisp->clock)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(bdisp->clock); + } + + ret = clk_prepare(bdisp->clock); + if (ret < 0) { + dev_err(dev, "clock prepare failed\n"); + bdisp->clock = ERR_PTR(-EINVAL); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to get IRQ resource\n"); + goto err_clk; + } + + ret = devm_request_threaded_irq(dev, res->start, bdisp_irq_handler, + bdisp_irq_thread, IRQF_ONESHOT, + pdev->name, bdisp); + if (ret) { + dev_err(dev, "failed to install irq\n"); + goto err_clk; + } + + /* v4l2 register */ + ret = v4l2_device_register(dev, &bdisp->v4l2_dev); + if (ret) { + dev_err(dev, "failed to register\n"); + goto err_clk; + } + + /* Debug */ + ret = bdisp_debugfs_create(bdisp); + if (ret) { + dev_err(dev, "failed to create debugfs\n"); + goto err_v4l2; + } + + /* Power management */ + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "failed to set PM\n"); + goto err_dbg; + } + + /* Continuous memory allocator */ + bdisp->alloc_ctx = vb2_dma_contig_init_ctx(dev); + if (IS_ERR(bdisp->alloc_ctx)) { + ret = PTR_ERR(bdisp->alloc_ctx); + goto err_pm; + } + + /* Filters */ + if (bdisp_hw_alloc_filters(bdisp->dev)) { + dev_err(bdisp->dev, "no memory for filters\n"); + ret = -ENOMEM; + goto err_vb2_dma; + } + + /* Register */ + ret = bdisp_register_device(bdisp); + if (ret) { + dev_err(dev, "failed to register\n"); + goto err_filter; + } + + dev_info(dev, "%s%d registered as /dev/video%d\n", BDISP_NAME, + bdisp->id, bdisp->vdev.num); + + pm_runtime_put(dev); + + return 0; + +err_filter: + bdisp_hw_free_filters(bdisp->dev); +err_vb2_dma: + vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx); +err_pm: + pm_runtime_put(dev); +err_dbg: + bdisp_debugfs_remove(bdisp); +err_v4l2: + v4l2_device_unregister(&bdisp->v4l2_dev); +err_clk: + if (!IS_ERR(bdisp->clock)) + clk_unprepare(bdisp->clock); + + return ret; +} + +static const struct of_device_id bdisp_match_types[] = { + { + .compatible = "st,stih407-bdisp", + }, + { /* end node */ } +}; + +MODULE_DEVICE_TABLE(of, bdisp_match_types); + +static struct platform_driver bdisp_driver = { + .probe = bdisp_probe, + .remove = bdisp_remove, + .driver = { + .name = BDISP_NAME, + .of_match_table = bdisp_match_types, + .pm = &bdisp_pm_ops, + }, +}; + +module_platform_driver(bdisp_driver); + +MODULE_DESCRIPTION("2D blitter for STMicroelectronics SoC"); +MODULE_AUTHOR("Fabien Dessenne "); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/platform/sti/bdisp/bdisp.h b/kernel/drivers/media/platform/sti/bdisp/bdisp.h new file mode 100644 index 000000000..0cf985772 --- /dev/null +++ b/kernel/drivers/media/platform/sti/bdisp/bdisp.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define BDISP_NAME "bdisp" + +/* + * Max nb of nodes in node-list: + * - 2 nodes to handle wide 4K pictures + * - 2 nodes to handle two planes (Y & CbCr) */ +#define MAX_OUTPUT_PLANES 2 +#define MAX_VERTICAL_STRIDES 2 +#define MAX_NB_NODE (MAX_OUTPUT_PLANES * MAX_VERTICAL_STRIDES) + +/* struct bdisp_ctrls - bdisp control set + * @hflip: horizontal flip + * @vflip: vertical flip + */ +struct bdisp_ctrls { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; +}; + +/** + * struct bdisp_fmt - driver's internal color format data + * @pixelformat:fourcc code for this format + * @nb_planes: number of planes (ex: [0]=RGB/Y - [1]=Cb/Cr, ...) + * @bpp: bits per pixel (general) + * @bpp_plane0: byte per pixel for the 1st plane + * @w_align: width alignment in pixel (multiple of) + * @h_align: height alignment in pixel (multiple of) + */ +struct bdisp_fmt { + u32 pixelformat; + u8 nb_planes; + u8 bpp; + u8 bpp_plane0; + u8 w_align; + u8 h_align; +}; + +/** + * struct bdisp_frame - frame properties + * + * @width: frame width (including padding) + * @height: frame height (including padding) + * @fmt: pointer to frame format descriptor + * @field: frame / field type + * @bytesperline: stride of the 1st plane + * @sizeimage: image size in bytes + * @colorspace: colorspace + * @crop: crop area + * @paddr: image physical addresses per plane ([0]=RGB/Y - [1]=Cb/Cr, ...) + */ +struct bdisp_frame { + u32 width; + u32 height; + const struct bdisp_fmt *fmt; + enum v4l2_field field; + u32 bytesperline; + u32 sizeimage; + enum v4l2_colorspace colorspace; + struct v4l2_rect crop; + dma_addr_t paddr[4]; +}; + +/** + * struct bdisp_request - bdisp request + * + * @src: source frame properties + * @dst: destination frame properties + * @hflip: horizontal flip + * @vflip: vertical flip + * @nb_req: number of run request + */ +struct bdisp_request { + struct bdisp_frame src; + struct bdisp_frame dst; + unsigned int hflip:1; + unsigned int vflip:1; + int nb_req; +}; + +/** + * struct bdisp_ctx - device context data + * + * @src: source frame properties + * @dst: destination frame properties + * @state: flags to keep track of user configuration + * @hflip: horizontal flip + * @vflip: vertical flip + * @bdisp_dev: the device this context applies to + * @node: node array + * @node_paddr: node physical address array + * @fh: v4l2 file handle + * @ctrl_handler: v4l2 controls handler + * @bdisp_ctrls: bdisp control set + * @ctrls_rdy: true if the control handler is initialized + */ +struct bdisp_ctx { + struct bdisp_frame src; + struct bdisp_frame dst; + u32 state; + unsigned int hflip:1; + unsigned int vflip:1; + struct bdisp_dev *bdisp_dev; + struct bdisp_node *node[MAX_NB_NODE]; + dma_addr_t node_paddr[MAX_NB_NODE]; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct bdisp_ctrls bdisp_ctrls; + bool ctrls_rdy; +}; + +/** + * struct bdisp_m2m_device - v4l2 memory-to-memory device data + * + * @vdev: video device node for v4l2 m2m mode + * @m2m_dev: v4l2 m2m device data + * @ctx: hardware context data + * @refcnt: reference counter + */ +struct bdisp_m2m_device { + struct video_device *vdev; + struct v4l2_m2m_dev *m2m_dev; + struct bdisp_ctx *ctx; + int refcnt; +}; + +/** + * struct bdisp_dbg - debug info + * + * @debugfs_entry: debugfs + * @copy_node: array of last used nodes + * @copy_request: last bdisp request + * @hw_start: start time of last HW request + * @last_duration: last HW processing duration in microsecs + * @min_duration: min HW processing duration in microsecs + * @max_duration: max HW processing duration in microsecs + * @tot_duration: total HW processing duration in microsecs + */ +struct bdisp_dbg { + struct dentry *debugfs_entry; + struct bdisp_node *copy_node[MAX_NB_NODE]; + struct bdisp_request copy_request; + ktime_t hw_start; + s64 last_duration; + s64 min_duration; + s64 max_duration; + s64 tot_duration; +}; + +/** + * struct bdisp_dev - abstraction for bdisp entity + * + * @v4l2_dev: v4l2 device + * @vdev: video device + * @pdev: platform device + * @dev: device + * @lock: mutex protecting this data structure + * @slock: spinlock protecting this data structure + * @id: device index + * @m2m: memory-to-memory V4L2 device information + * @state: flags used to synchronize m2m and capture mode operation + * @alloc_ctx: videobuf2 memory allocator context + * @clock: IP clock + * @regs: registers + * @irq_queue: interrupt handler waitqueue + * @work_queue: workqueue to handle timeouts + * @timeout_work: IRQ timeout structure + * @dbg: debug info + */ +struct bdisp_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct platform_device *pdev; + struct device *dev; + spinlock_t slock; + struct mutex lock; + u16 id; + struct bdisp_m2m_device m2m; + unsigned long state; + struct vb2_alloc_ctx *alloc_ctx; + struct clk *clock; + void __iomem *regs; + wait_queue_head_t irq_queue; + struct workqueue_struct *work_queue; + struct delayed_work timeout_work; + struct bdisp_dbg dbg; +}; + +void bdisp_hw_free_nodes(struct bdisp_ctx *ctx); +int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx); +void bdisp_hw_free_filters(struct device *dev); +int bdisp_hw_alloc_filters(struct device *dev); +int bdisp_hw_reset(struct bdisp_dev *bdisp); +int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp); +int bdisp_hw_update(struct bdisp_ctx *ctx); + +void bdisp_debugfs_remove(struct bdisp_dev *bdisp); +int bdisp_debugfs_create(struct bdisp_dev *bdisp); +void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp); +void bdisp_dbg_perf_end(struct bdisp_dev *bdisp); diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/Kconfig b/kernel/drivers/media/platform/sti/c8sectpfe/Kconfig new file mode 100644 index 000000000..7420a5057 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -0,0 +1,27 @@ +config DVB_C8SECTPFE + tristate "STMicroelectronics C8SECTPFE DVB support" + depends on PINCTRL && DVB_CORE && I2C + depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST + select FW_LOADER + select DEBUG_FS + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT + + ---help--- + This adds support for DVB front-end cards connected + to TS inputs of STiH407/410 SoC. + + The driver currently supports C8SECTPFE's TS input block, + memdma engine, and HW PID filtering. + + Supported DVB front-end cards are: + - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212) + - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board + + To compile this driver as a module, choose M here: the + module will be called c8sectpfe. diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/Makefile b/kernel/drivers/media/platform/sti/c8sectpfe/Makefile new file mode 100644 index 000000000..b578c7cb4 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/Makefile @@ -0,0 +1,9 @@ +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \ + c8sectpfe-debugfs.o + +obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o + +ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ \ + -Idrivers/media/tuners/ diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c new file mode 100644 index 000000000..95223ab71 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c @@ -0,0 +1,265 @@ +/* + * c8sectpfe-common.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "c8sectpfe-common.h" +#include "c8sectpfe-core.h" +#include "c8sectpfe-dvb.h" + +static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap, + void *start_feed, void *stop_feed, + struct c8sectpfei *fei) +{ + int result; + + demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + + demux->dvb_demux.priv = demux; + demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL; + demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL; + + demux->dvb_demux.start_feed = start_feed; + demux->dvb_demux.stop_feed = stop_feed; + demux->dvb_demux.write_to_decoder = NULL; + + result = dvb_dmx_init(&demux->dvb_demux); + if (result < 0) { + dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n", + result); + goto err_dmx; + } + + demux->dmxdev.filternum = demux->dvb_demux.filternum; + demux->dmxdev.demux = &demux->dvb_demux.dmx; + demux->dmxdev.capabilities = 0; + + result = dvb_dmxdev_init(&demux->dmxdev, adap); + if (result < 0) { + dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n", + result); + + goto err_dmxdev; + } + + demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index; + + result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + if (result < 0) { + dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result); + goto err_fe_hw; + } + + demux->mem_frontend.source = DMX_MEMORY_FE; + result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); + if (result < 0) { + dev_err(fei->dev, "add_frontend failed (%d)\n", result); + goto err_fe_mem; + } + + result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + if (result < 0) { + dev_err(fei->dev, "connect_frontend (%d)\n", result); + goto err_fe_con; + } + + return 0; + +err_fe_con: + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); +err_fe_mem: + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); +err_fe_hw: + dvb_dmxdev_release(&demux->dmxdev); +err_dmxdev: + dvb_dmx_release(&demux->dvb_demux); +err_dmx: + return result; + +} + +static void unregister_dvb(struct stdemux *demux) +{ + + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); + + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + + dvb_dmxdev_release(&demux->dmxdev); + + dvb_dmx_release(&demux->dvb_demux); +} + +static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei, + void *start_feed, + void *stop_feed) +{ + struct c8sectpfe *c8sectpfe; + int result; + int i, j; + + short int ids[] = { -1 }; + + c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL); + if (!c8sectpfe) + goto err1; + + mutex_init(&c8sectpfe->lock); + + c8sectpfe->device = fei->dev; + + result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe", + THIS_MODULE, fei->dev, ids); + if (result < 0) { + dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n", + result); + goto err2; + } + + c8sectpfe->adapter.priv = fei; + + for (i = 0; i < fei->tsin_count; i++) { + + c8sectpfe->demux[i].tsin_index = i; + c8sectpfe->demux[i].c8sectpfei = fei; + + result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter, + start_feed, stop_feed, fei); + if (result < 0) { + dev_err(fei->dev, + "register_dvb feed=%d failed (errno = %d)\n", + result, i); + + /* we take a all or nothing approach */ + for (j = 0; j < i; j++) + unregister_dvb(&c8sectpfe->demux[j]); + goto err3; + } + } + + c8sectpfe->num_feeds = fei->tsin_count; + + return c8sectpfe; +err3: + dvb_unregister_adapter(&c8sectpfe->adapter); +err2: + kfree(c8sectpfe); +err1: + return NULL; +}; + +static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe) +{ + int i; + + if (!c8sectpfe) + return; + + for (i = 0; i < c8sectpfe->num_feeds; i++) + unregister_dvb(&c8sectpfe->demux[i]); + + dvb_unregister_adapter(&c8sectpfe->adapter); + + kfree(c8sectpfe); +}; + +void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, + struct c8sectpfei *fei) +{ + int n; + struct channel_info *tsin; + + for (n = 0; n < fei->tsin_count; n++) { + + tsin = fei->channel_data[n]; + + if (tsin && tsin->frontend) { + dvb_unregister_frontend(tsin->frontend); + dvb_frontend_detach(tsin->frontend); + } + + if (tsin && tsin->i2c_adapter) + i2c_put_adapter(tsin->i2c_adapter); + + if (tsin && tsin->i2c_client) { + if (tsin->i2c_client->dev.driver->owner) + module_put(tsin->i2c_client->dev.driver->owner); + i2c_unregister_device(tsin->i2c_client); + } + } + + c8sectpfe_delete(c8sectpfe); +}; + +int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, + struct c8sectpfei *fei, + void *start_feed, + void *stop_feed) +{ + struct channel_info *tsin; + struct dvb_frontend *frontend; + int n, res; + + *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed); + if (!*c8sectpfe) + return -ENOMEM; + + for (n = 0; n < fei->tsin_count; n++) { + tsin = fei->channel_data[n]; + + res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n); + if (res) + goto err; + + res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend); + if (res < 0) { + dev_err(fei->dev, "dvb_register_frontend failed (%d)\n", + res); + goto err; + } + + tsin->frontend = frontend; + } + + return 0; + +err: + c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei); + return res; +} diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h new file mode 100644 index 000000000..da21c0ac0 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h @@ -0,0 +1,64 @@ +/* + * c8sectpfe-common.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_COMMON_H_ +#define _C8SECTPFE_COMMON_H_ + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +/* Maximum number of channels */ +#define C8SECTPFE_MAXADAPTER (4) +#define C8SECTPFE_MAXCHANNEL 64 +#define STPTI_MAXCHANNEL 64 + +#define MAX_INPUTBLOCKS 7 + +struct c8sectpfe; +struct stdemux; + +struct stdemux { + struct dvb_demux dvb_demux; + struct dmxdev dmxdev; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int tsin_index; + int running_feed_count; + struct c8sectpfei *c8sectpfei; +}; + +struct c8sectpfe { + struct stdemux demux[MAX_INPUTBLOCKS]; + struct mutex lock; + struct dvb_adapter adapter; + struct device *device; + int mapping; + int num_feeds; +}; + +/* Channel registration */ +int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, + struct c8sectpfei *fei, + void *start_feed, + void *stop_feed); + +void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, + struct c8sectpfei *fei); + +#endif diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c new file mode 100644 index 000000000..8490a65ae --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -0,0 +1,1235 @@ +/* + * c8sectpfe-core.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author:Peter Bennett + * Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c8sectpfe-core.h" +#include "c8sectpfe-common.h" +#include "c8sectpfe-debugfs.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#define FIRMWARE_MEMDMA "pti_memdma_h407.elf" +MODULE_FIRMWARE(FIRMWARE_MEMDMA); + +#define PID_TABLE_SIZE 1024 +#define POLL_MSECS 50 + +static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei); + +#define TS_PKT_SIZE 188 +#define HEADER_SIZE (4) +#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE) + +#define FEI_ALIGNMENT (32) +/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */ +#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340) + +#define FIFO_LEN 1024 + +static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei) +{ + struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei; + struct channel_info *channel; + int chan_num; + + /* iterate through input block channels */ + for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) { + channel = fei->channel_data[chan_num]; + + /* is this descriptor initialised and TP enabled */ + if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) + tasklet_schedule(&channel->tsklet); + } + + fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); + add_timer(&fei->timer); +} + +static void channel_swdemux_tsklet(unsigned long data) +{ + struct channel_info *channel = (struct channel_info *)data; + struct c8sectpfei *fei = channel->fei; + unsigned long wp, rp; + int pos, num_packets, n, size; + u8 *buf; + + if (unlikely(!channel || !channel->irec)) + return; + + wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0)); + rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0)); + + pos = rp - channel->back_buffer_busaddr; + + /* has it wrapped */ + if (wp < rp) + wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE; + + size = wp - rp; + num_packets = size / PACKET_SIZE; + + /* manage cache so data is visible to CPU */ + dma_sync_single_for_cpu(fei->dev, + rp, + size, + DMA_FROM_DEVICE); + + buf = (u8 *) channel->back_buffer_aligned; + + dev_dbg(fei->dev, + "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t" + "rp=0x%lx, wp=0x%lx\n", + channel->tsin_id, channel, num_packets, buf, pos, rp, wp); + + for (n = 0; n < num_packets; n++) { + dvb_dmx_swfilter_packets( + &fei->c8sectpfe[0]-> + demux[channel->demux_mapping].dvb_demux, + &buf[pos], 1); + + pos += PACKET_SIZE; + } + + /* advance the read pointer */ + if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE)) + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSRP_TP(0)); + else + writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0)); +} + +static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct stdemux *stdemux = (struct stdemux *)demux->priv; + struct c8sectpfei *fei = stdemux->c8sectpfei; + struct channel_info *channel; + u32 tmp; + unsigned long *bitmap; + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + break; + case DMX_TYPE_SEC: + break; + default: + dev_err(fei->dev, "%s:%d Error bailing\n" + , __func__, __LINE__); + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_PES_VIDEO: + case DMX_PES_AUDIO: + case DMX_PES_TELETEXT: + case DMX_PES_PCR: + case DMX_PES_OTHER: + break; + default: + dev_err(fei->dev, "%s:%d Error bailing\n" + , __func__, __LINE__); + return -EINVAL; + } + } + + if (!atomic_read(&fei->fw_loaded)) { + dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); + return -EINVAL; + } + + mutex_lock(&fei->lock); + + channel = fei->channel_data[stdemux->tsin_index]; + + bitmap = (unsigned long *) channel->pid_buffer_aligned; + + /* 8192 is a special PID */ + if (dvbdmxfeed->pid == 8192) { + tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + tmp &= ~C8SECTPFE_PID_ENABLE; + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + + } else { + bitmap_set(bitmap, dvbdmxfeed->pid, 1); + } + + /* manage cache so PID bitmap is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + channel->active = 1; + + if (fei->global_feed_count == 0) { + fei->timer.expires = jiffies + + msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS)); + + add_timer(&fei->timer); + } + + if (stdemux->running_feed_count == 0) { + + dev_dbg(fei->dev, "Starting channel=%p\n", channel); + + tasklet_init(&channel->tsklet, channel_swdemux_tsklet, + (unsigned long) channel); + + /* Reset the internal inputblock sram pointers */ + writel(channel->fifo, + fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id)); + writel(channel->fifo + FIFO_LEN - 1, + fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id)); + + writel(channel->fifo, + fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id)); + writel(channel->fifo, + fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id)); + + + /* reset read / write memdma ptrs for this channel */ + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSBASE_TP(0)); + + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSWP_TP(0)); + + /* Issue a reset and enable InputBlock */ + writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET + , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); + + /* and enable the tp */ + writel(0x1, channel->irec + DMA_PRDS_TPENABLE); + + dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n" + , __func__, __LINE__, stdemux); + } + + stdemux->running_feed_count++; + fei->global_feed_count++; + + mutex_unlock(&fei->lock); + + return 0; +} + +static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + + struct dvb_demux *demux = dvbdmxfeed->demux; + struct stdemux *stdemux = (struct stdemux *)demux->priv; + struct c8sectpfei *fei = stdemux->c8sectpfei; + struct channel_info *channel; + int idlereq; + u32 tmp; + int ret; + unsigned long *bitmap; + + if (!atomic_read(&fei->fw_loaded)) { + dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); + return -EINVAL; + } + + mutex_lock(&fei->lock); + + channel = fei->channel_data[stdemux->tsin_index]; + + bitmap = (unsigned long *) channel->pid_buffer_aligned; + + if (dvbdmxfeed->pid == 8192) { + tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + tmp |= C8SECTPFE_PID_ENABLE; + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + } else { + bitmap_clear(bitmap, dvbdmxfeed->pid, 1); + } + + /* manage cache so data is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + if (--stdemux->running_feed_count == 0) { + + channel = fei->channel_data[stdemux->tsin_index]; + + /* TP re-configuration on page 168 of functional spec */ + + /* disable IB (prevents more TS data going to memdma) */ + writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); + + /* disable this channels descriptor */ + writel(0, channel->irec + DMA_PRDS_TPENABLE); + + tasklet_disable(&channel->tsklet); + + /* now request memdma channel goes idle */ + idlereq = (1 << channel->tsin_id) | IDLEREQ; + writel(idlereq, fei->io + DMA_IDLE_REQ); + + /* wait for idle irq handler to signal completion */ + ret = wait_for_completion_timeout(&channel->idle_completion, + msecs_to_jiffies(100)); + + if (ret == 0) + dev_warn(fei->dev, + "Timeout waiting for idle irq on tsin%d\n", + channel->tsin_id); + + reinit_completion(&channel->idle_completion); + + /* reset read / write ptrs for this channel */ + + writel(channel->back_buffer_busaddr, + channel->irec + DMA_PRDS_BUSBASE_TP(0)); + + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(channel->back_buffer_busaddr, + channel->irec + DMA_PRDS_BUSWP_TP(0)); + + dev_dbg(fei->dev, + "%s:%d stopping DMA feed on stdemux=%p channel=%d\n", + __func__, __LINE__, stdemux, channel->tsin_id); + + /* turn off all PIDS in the bitmap */ + memset((void *)channel->pid_buffer_aligned + , 0x00, PID_TABLE_SIZE); + + /* manage cache so data is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + channel->active = 0; + } + + if (--fei->global_feed_count == 0) { + dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n" + , __func__, __LINE__, fei->global_feed_count); + + del_timer(&fei->timer); + } + + mutex_unlock(&fei->lock); + + return 0; +} + +static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num) +{ + int i; + + for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) { + if (!fei->channel_data[i]) + continue; + + if (fei->channel_data[i]->tsin_id == tsin_num) + return fei->channel_data[i]; + } + + return NULL; +} + +static void c8sectpfe_getconfig(struct c8sectpfei *fei) +{ + struct c8sectpfe_hw *hw = &fei->hw_stats; + + hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB); + hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB); + hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS); + hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT); + hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC); + hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM); + hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP); + + dev_info(fei->dev, "C8SECTPFE hw supports the following:\n"); + dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib); + dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib); + dev_info(fei->dev, "Software Transport Stream Inputs: %d\n" + , hw->num_swts); + dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout); + dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc); + dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram); + dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n" + , hw->num_tp); +} + +static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv) +{ + struct c8sectpfei *fei = priv; + struct channel_info *chan; + int bit; + unsigned long tmp = readl(fei->io + DMA_IDLE_REQ); + + /* page 168 of functional spec: Clear the idle request + by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */ + + /* signal idle completion */ + for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) { + + chan = find_channel(fei, bit); + + if (chan) + complete(&chan->idle_completion); + } + + writel(0, fei->io + DMA_IDLE_REQ); + + return IRQ_HANDLED; +} + + +static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin) +{ + if (!fei || !tsin) + return; + + if (tsin->back_buffer_busaddr) + if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) + dma_unmap_single(fei->dev, tsin->back_buffer_busaddr, + FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL); + + kfree(tsin->back_buffer_start); + + if (tsin->pid_buffer_busaddr) + if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) + dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr, + PID_TABLE_SIZE, DMA_BIDIRECTIONAL); + + kfree(tsin->pid_buffer_start); +} + +#define MAX_NAME 20 + +static int configure_memdma_and_inputblock(struct c8sectpfei *fei, + struct channel_info *tsin) +{ + int ret; + u32 tmp; + char tsin_pin_name[MAX_NAME]; + + if (!fei || !tsin) + return -EINVAL; + + dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n" + , __func__, __LINE__, tsin, tsin->tsin_id); + + init_completion(&tsin->idle_completion); + + tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + + FEI_ALIGNMENT, GFP_KERNEL); + + if (!tsin->back_buffer_start) { + ret = -ENOMEM; + goto err_unmap; + } + + /* Ensure backbuffer is 32byte aligned */ + tsin->back_buffer_aligned = tsin->back_buffer_start + + FEI_ALIGNMENT; + + tsin->back_buffer_aligned = (void *) + (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F); + + tsin->back_buffer_busaddr = dma_map_single(fei->dev, + (void *)tsin->back_buffer_aligned, + FEI_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) { + dev_err(fei->dev, "failed to map back_buffer\n"); + ret = -EFAULT; + goto err_unmap; + } + + /* + * The pid buffer can be configured (in hw) for byte or bit + * per pid. By powers of deduction we conclude stih407 family + * is configured (at SoC design stage) for bit per pid. + */ + tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL); + + if (!tsin->pid_buffer_start) { + ret = -ENOMEM; + goto err_unmap; + } + + /* + * PID buffer needs to be aligned to size of the pid table + * which at bit per pid is 1024 bytes (8192 pids / 8). + * PIDF_BASE register enforces this alignment when writing + * the register. + */ + + tsin->pid_buffer_aligned = tsin->pid_buffer_start + + PID_TABLE_SIZE; + + tsin->pid_buffer_aligned = (void *) + (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff); + + tsin->pid_buffer_busaddr = dma_map_single(fei->dev, + tsin->pid_buffer_aligned, + PID_TABLE_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) { + dev_err(fei->dev, "failed to map pid_bitmap\n"); + ret = -EFAULT; + goto err_unmap; + } + + /* manage cache so pid bitmap is visible to HW */ + dma_sync_single_for_device(fei->dev, + tsin->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id, + (tsin->serial_not_parallel ? "serial" : "parallel")); + + tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name); + if (IS_ERR(tsin->pstate)) { + dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n" + , __func__, tsin_pin_name); + ret = PTR_ERR(tsin->pstate); + goto err_unmap; + } + + ret = pinctrl_select_state(fei->pinctrl, tsin->pstate); + + if (ret) { + dev_err(fei->dev, "%s: pinctrl_select_state failed\n" + , __func__); + goto err_unmap; + } + + /* Enable this input block */ + tmp = readl(fei->io + SYS_INPUT_CLKEN); + tmp |= BIT(tsin->tsin_id); + writel(tmp, fei->io + SYS_INPUT_CLKEN); + + if (tsin->serial_not_parallel) + tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL; + + if (tsin->invert_ts_clk) + tmp |= C8SECTPFE_INVERT_TSCLK; + + if (tsin->async_not_sync) + tmp |= C8SECTPFE_ASYNC_NOT_SYNC; + + tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB; + + writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id)); + + writel(C8SECTPFE_SYNC(0x9) | + C8SECTPFE_DROP(0x9) | + C8SECTPFE_TOKEN(0x47), + fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id)); + + writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id)); + + /* Place the FIFO's at the end of the irec descriptors */ + + tsin->fifo = (tsin->tsin_id * FIFO_LEN); + + writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)); + writel(tsin->fifo + FIFO_LEN - 1, + fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)); + + writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)); + writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)); + + writel(tsin->pid_buffer_busaddr, + fei->io + PIDF_BASE(tsin->tsin_id)); + + dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", + tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), + &tsin->pid_buffer_busaddr); + + /* Configure and enable HW PID filtering */ + + /* + * The PID value is created by assembling the first 8 bytes of + * the TS packet into a 64-bit word in big-endian format. A + * slice of that 64-bit word is taken from + * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET. + */ + tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13) + | C8SECTPFE_PID_OFFSET(40)); + + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id)); + + dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n", + tsin->tsin_id, + readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id))); + + /* Get base addpress of pointer record block from DMEM */ + tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + + readl(fei->io + DMA_PTRREC_BASE); + + /* fill out pointer record data structure */ + + /* advance pointer record block to our channel */ + tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE); + + writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE); + + writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP); + + writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE); + + writel(0x1, tsin->irec + DMA_PRDS_TPENABLE); + + /* read/write pointers with physical bus address */ + + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0)); + + tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); + + /* initialize tasklet */ + tasklet_init(&tsin->tsklet, channel_swdemux_tsklet, + (unsigned long) tsin); + + return 0; + +err_unmap: + free_input_block(fei, tsin); + return ret; +} + +static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv) +{ + struct c8sectpfei *fei = priv; + + dev_err(fei->dev, "%s: error handling not yet implemented\n" + , __func__); + + /* + * TODO FIXME we should detect some error conditions here + * and ideally so something about them! + */ + + return IRQ_HANDLED; +} + +static int c8sectpfe_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child, *np = dev->of_node; + struct c8sectpfei *fei; + struct resource *res; + int ret, index = 0; + struct channel_info *tsin; + + /* Allocate the c8sectpfei structure */ + fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL); + if (!fei) + return -ENOMEM; + + fei->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe"); + fei->io = devm_ioremap_resource(dev, res); + if (IS_ERR(fei->io)) + return PTR_ERR(fei->io); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "c8sectpfe-ram"); + fei->sram = devm_ioremap_resource(dev, res); + if (IS_ERR(fei->sram)) + return PTR_ERR(fei->sram); + + fei->sram_size = res->end - res->start; + + fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq"); + if (fei->idle_irq < 0) { + dev_err(dev, "Can't get c8sectpfe-idle-irq\n"); + return fei->idle_irq; + } + + fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq"); + if (fei->error_irq < 0) { + dev_err(dev, "Can't get c8sectpfe-error-irq\n"); + return fei->error_irq; + } + + platform_set_drvdata(pdev, fei); + + fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe"); + if (IS_ERR(fei->c8sectpfeclk)) { + dev_err(dev, "c8sectpfe clk not found\n"); + return PTR_ERR(fei->c8sectpfeclk); + } + + ret = clk_prepare_enable(fei->c8sectpfeclk); + if (ret) { + dev_err(dev, "Failed to enable c8sectpfe clock\n"); + return ret; + } + + /* to save power disable all IP's (on by default) */ + writel(0, fei->io + SYS_INPUT_CLKEN); + + /* Enable memdma clock */ + writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN); + + /* clear internal sram */ + memset_io(fei->sram, 0x0, fei->sram_size); + + c8sectpfe_getconfig(fei); + + ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler, + 0, "c8sectpfe-idle-irq", fei); + if (ret) { + dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n"); + goto err_clk_disable; + } + + ret = devm_request_irq(dev, fei->error_irq, + c8sectpfe_error_irq_handler, 0, + "c8sectpfe-error-irq", fei); + if (ret) { + dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n"); + goto err_clk_disable; + } + + fei->tsin_count = of_get_child_count(np); + + if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN || + fei->tsin_count > fei->hw_stats.num_ib) { + + dev_err(dev, "More tsin declared than exist on SoC!\n"); + ret = -EINVAL; + goto err_clk_disable; + } + + fei->pinctrl = devm_pinctrl_get(dev); + + if (IS_ERR(fei->pinctrl)) { + dev_err(dev, "Error getting tsin pins\n"); + ret = PTR_ERR(fei->pinctrl); + goto err_clk_disable; + } + + for_each_child_of_node(np, child) { + struct device_node *i2c_bus; + + fei->channel_data[index] = devm_kzalloc(dev, + sizeof(struct channel_info), + GFP_KERNEL); + + if (!fei->channel_data[index]) { + ret = -ENOMEM; + goto err_clk_disable; + } + + tsin = fei->channel_data[index]; + + tsin->fei = fei; + + ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); + if (ret) { + dev_err(&pdev->dev, "No tsin_num found\n"); + goto err_clk_disable; + } + + /* sanity check value */ + if (tsin->tsin_id > fei->hw_stats.num_ib) { + dev_err(&pdev->dev, + "tsin-num %d specified greater than number\n\t" + "of input block hw in SoC! (%d)", + tsin->tsin_id, fei->hw_stats.num_ib); + ret = -EINVAL; + goto err_clk_disable; + } + + tsin->invert_ts_clk = of_property_read_bool(child, + "invert-ts-clk"); + + tsin->serial_not_parallel = of_property_read_bool(child, + "serial-not-parallel"); + + tsin->async_not_sync = of_property_read_bool(child, + "async-not-sync"); + + ret = of_property_read_u32(child, "dvb-card", + &tsin->dvb_card); + if (ret) { + dev_err(&pdev->dev, "No dvb-card found\n"); + goto err_clk_disable; + } + + i2c_bus = of_parse_phandle(child, "i2c-bus", 0); + if (!i2c_bus) { + dev_err(&pdev->dev, "No i2c-bus found\n"); + goto err_clk_disable; + } + tsin->i2c_adapter = + of_find_i2c_adapter_by_node(i2c_bus); + if (!tsin->i2c_adapter) { + dev_err(&pdev->dev, "No i2c adapter found\n"); + of_node_put(i2c_bus); + goto err_clk_disable; + } + of_node_put(i2c_bus); + + tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0); + + ret = gpio_is_valid(tsin->rst_gpio); + if (!ret) { + dev_err(dev, + "reset gpio for tsin%d not valid (gpio=%d)\n", + tsin->tsin_id, tsin->rst_gpio); + goto err_clk_disable; + } + + ret = devm_gpio_request_one(dev, tsin->rst_gpio, + GPIOF_OUT_INIT_LOW, "NIM reset"); + if (ret && ret != -EBUSY) { + dev_err(dev, "Can't request tsin%d reset gpio\n" + , fei->channel_data[index]->tsin_id); + goto err_clk_disable; + } + + if (!ret) { + /* toggle reset lines */ + gpio_direction_output(tsin->rst_gpio, 0); + usleep_range(3500, 5000); + gpio_direction_output(tsin->rst_gpio, 1); + usleep_range(3000, 5000); + } + + tsin->demux_mapping = index; + + dev_dbg(fei->dev, + "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t" + "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n", + fei->channel_data[index], index, + tsin->tsin_id, tsin->invert_ts_clk, + tsin->serial_not_parallel, tsin->async_not_sync, + tsin->dvb_card); + + index++; + } + + /* Setup timer interrupt */ + init_timer(&fei->timer); + fei->timer.function = c8sectpfe_timer_interrupt; + fei->timer.data = (unsigned long)fei; + + mutex_init(&fei->lock); + + /* Get the configuration information about the tuners */ + ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0], + (void *)fei, + c8sectpfe_start_feed, + c8sectpfe_stop_feed); + if (ret) { + dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n", + ret); + goto err_clk_disable; + } + + /* ensure all other init has been done before requesting firmware */ + ret = load_c8sectpfe_fw_step1(fei); + if (ret) { + dev_err(dev, "Couldn't load slim core firmware\n"); + goto err_clk_disable; + } + + c8sectpfe_debugfs_init(fei); + + return 0; + +err_clk_disable: + /* TODO uncomment when upstream has taken a reference on this clk */ + /*clk_disable_unprepare(fei->c8sectpfeclk);*/ + return ret; +} + +static int c8sectpfe_remove(struct platform_device *pdev) +{ + struct c8sectpfei *fei = platform_get_drvdata(pdev); + struct channel_info *channel; + int i; + + wait_for_completion(&fei->fw_ack); + + c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei); + + /* + * Now loop through and un-configure each of the InputBlock resources + */ + for (i = 0; i < fei->tsin_count; i++) { + channel = fei->channel_data[i]; + free_input_block(fei, channel); + } + + c8sectpfe_debugfs_exit(fei); + + dev_info(fei->dev, "Stopping memdma SLIM core\n"); + if (readl(fei->io + DMA_CPU_RUN)) + writel(0x0, fei->io + DMA_CPU_RUN); + + /* unclock all internal IP's */ + if (readl(fei->io + SYS_INPUT_CLKEN)) + writel(0, fei->io + SYS_INPUT_CLKEN); + + if (readl(fei->io + SYS_OTHER_CLKEN)) + writel(0, fei->io + SYS_OTHER_CLKEN); + + /* TODO uncomment when upstream has taken a reference on this clk */ + /* + if (fei->c8sectpfeclk) + clk_disable_unprepare(fei->c8sectpfeclk); + */ + + return 0; +} + + +static int configure_channels(struct c8sectpfei *fei) +{ + int index = 0, ret; + struct channel_info *tsin; + struct device_node *child, *np = fei->dev->of_node; + + /* iterate round each tsin and configure memdma descriptor and IB hw */ + for_each_child_of_node(np, child) { + + tsin = fei->channel_data[index]; + + ret = configure_memdma_and_inputblock(fei, + fei->channel_data[index]); + + if (ret) { + dev_err(fei->dev, + "configure_memdma_and_inputblock failed\n"); + goto err_unmap; + } + index++; + } + + return 0; + +err_unmap: + for (index = 0; index < fei->tsin_count; index++) { + tsin = fei->channel_data[index]; + free_input_block(fei, tsin); + } + return ret; +} + +static int +c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw) +{ + struct elf32_hdr *ehdr; + char class; + + if (!fw) { + dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA); + return -EINVAL; + } + + if (fw->size < sizeof(struct elf32_hdr)) { + dev_err(fei->dev, "Image is too small\n"); + return -EINVAL; + } + + ehdr = (struct elf32_hdr *)fw->data; + + /* We only support ELF32 at this point */ + class = ehdr->e_ident[EI_CLASS]; + if (class != ELFCLASS32) { + dev_err(fei->dev, "Unsupported class: %d\n", class); + return -EINVAL; + } + + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { + dev_err(fei->dev, "Unsupported firmware endianness\n"); + return -EINVAL; + } + + if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { + dev_err(fei->dev, "Image is too small\n"); + return -EINVAL; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(fei->dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + + /* Check ELF magic */ + ehdr = (Elf32_Ehdr *)fw->data; + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || + ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || + ehdr->e_ident[EI_MAG3] != ELFMAG3) { + dev_err(fei->dev, "Invalid ELF magic\n"); + return -EINVAL; + } + + if (ehdr->e_type != ET_EXEC) { + dev_err(fei->dev, "Unsupported ELF header type\n"); + return -EINVAL; + } + + if (ehdr->e_phoff > fw->size) { + dev_err(fei->dev, "Firmware size is too small\n"); + return -EINVAL; + } + + return 0; +} + + +static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, + const struct firmware *fw, u8 __iomem *dest, + int seg_num) +{ + const u8 *imem_src = fw->data + phdr->p_offset; + int i; + + /* + * For IMEM segments, the segment contains 24-bit + * instructions which must be padded to 32-bit + * instructions before being written. The written + * segment is padded with NOP instructions. + */ + + dev_dbg(fei->dev, + "Loading IMEM segment %d 0x%08x\n\t" + " (0x%x bytes) -> 0x%p (0x%x bytes)\n", seg_num, + phdr->p_paddr, phdr->p_filesz, + dest, phdr->p_memsz + phdr->p_memsz / 3); + + for (i = 0; i < phdr->p_filesz; i++) { + + writeb(readb((void __iomem *)imem_src), (void __iomem *)dest); + + /* Every 3 bytes, add an additional + * padding zero in destination */ + if (i % 3 == 2) { + dest++; + writeb(0x00, (void __iomem *)dest); + } + + dest++; + imem_src++; + } +} + +static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, + const struct firmware *fw, u8 __iomem *dst, int seg_num) +{ + /* + * For DMEM segments copy the segment data from the ELF + * file and pad segment with zeroes + */ + + dev_dbg(fei->dev, + "Loading DMEM segment %d 0x%08x\n\t" + "(0x%x bytes) -> 0x%p (0x%x bytes)\n", + seg_num, phdr->p_paddr, phdr->p_filesz, + dst, phdr->p_memsz); + + memcpy((void __force *)dst, (void *)fw->data + phdr->p_offset, + phdr->p_filesz); + + memset((void __force *)dst + phdr->p_filesz, 0, + phdr->p_memsz - phdr->p_filesz); +} + +static int load_slim_core_fw(const struct firmware *fw, void *context) +{ + struct c8sectpfei *fei = context; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + u8 __iomem *dst; + int err = 0, i; + + if (!fw || !context) + return -EINVAL; + + ehdr = (Elf32_Ehdr *)fw->data; + phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + + /* Only consider LOAD segments */ + if (phdr->p_type != PT_LOAD) + continue; + + /* + * Check segment is contained within the fw->data buffer + */ + if (phdr->p_offset + phdr->p_filesz > fw->size) { + dev_err(fei->dev, + "Segment %d is outside of firmware file\n", i); + err = -EINVAL; + break; + } + + /* + * MEMDMA IMEM has executable flag set, otherwise load + * this segment into DMEM. + * + */ + + if (phdr->p_flags & PF_X) { + dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM; + /* + * The Slim ELF file uses 32-bit word addressing for + * load offsets. + */ + dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); + load_imem_segment(fei, phdr, fw, dst, i); + } else { + dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM; + /* + * The Slim ELF file uses 32-bit word addressing for + * load offsets. + */ + dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); + load_dmem_segment(fei, phdr, fw, dst, i); + } + } + + release_firmware(fw); + return err; +} + +static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) +{ + struct c8sectpfei *fei = context; + int err; + + err = c8sectpfe_elf_sanity_check(fei, fw); + if (err) { + dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" + , err); + goto err; + } + + err = load_slim_core_fw(fw, context); + if (err) { + dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); + goto err; + } + + /* now the firmware is loaded configure the input blocks */ + err = configure_channels(fei); + if (err) { + dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); + goto err; + } + + /* + * STBus target port can access IMEM and DMEM ports + * without waiting for CPU + */ + writel(0x1, fei->io + DMA_PER_STBUS_SYNC); + + dev_info(fei->dev, "Boot the memdma SLIM core\n"); + writel(0x1, fei->io + DMA_CPU_RUN); + + atomic_set(&fei->fw_loaded, 1); +err: + complete_all(&fei->fw_ack); +} + +static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) +{ + int err; + + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); + + init_completion(&fei->fw_ack); + atomic_set(&fei->fw_loaded, 0); + + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + FIRMWARE_MEMDMA, fei->dev, GFP_KERNEL, fei, + load_c8sectpfe_fw_cb); + + if (err) { + dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err); + complete_all(&fei->fw_ack); + return err; + } + + return 0; +} + +static const struct of_device_id c8sectpfe_match[] = { + { .compatible = "st,stih407-c8sectpfe" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, c8sectpfe_match); + +static struct platform_driver c8sectpfe_driver = { + .driver = { + .name = "c8sectpfe", + .of_match_table = of_match_ptr(c8sectpfe_match), + }, + .probe = c8sectpfe_probe, + .remove = c8sectpfe_remove, +}; + +module_platform_driver(c8sectpfe_driver); + +MODULE_AUTHOR("Peter Bennett "); +MODULE_AUTHOR("Peter Griffin "); +MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h new file mode 100644 index 000000000..39e7a221a --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h @@ -0,0 +1,288 @@ +/* + * c8sectpfe-core.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author:Peter Bennett + * Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_CORE_H_ +#define _C8SECTPFE_CORE_H_ + +#define C8SECTPFEI_MAXCHANNEL 16 +#define C8SECTPFEI_MAXADAPTER 3 + +#define C8SECTPFE_MAX_TSIN_CHAN 8 + +struct channel_info { + + int tsin_id; + bool invert_ts_clk; + bool serial_not_parallel; + bool async_not_sync; + int i2c; + int dvb_card; + + int rst_gpio; + + struct i2c_adapter *i2c_adapter; + struct i2c_adapter *tuner_i2c; + struct i2c_adapter *lnb_i2c; + struct i2c_client *i2c_client; + struct dvb_frontend *frontend; + + struct pinctrl_state *pstate; + + int demux_mapping; + int active; + + void *back_buffer_start; + void *back_buffer_aligned; + dma_addr_t back_buffer_busaddr; + + void *pid_buffer_start; + void *pid_buffer_aligned; + dma_addr_t pid_buffer_busaddr; + + unsigned long fifo; + + struct completion idle_completion; + struct tasklet_struct tsklet; + + struct c8sectpfei *fei; + void __iomem *irec; + +}; + +struct c8sectpfe_hw { + int num_ib; + int num_mib; + int num_swts; + int num_tsout; + int num_ccsc; + int num_ram; + int num_tp; +}; + +struct c8sectpfei { + + struct device *dev; + struct pinctrl *pinctrl; + + struct dentry *root; + struct debugfs_regset32 *regset; + struct completion fw_ack; + atomic_t fw_loaded; + + int tsin_count; + + struct c8sectpfe_hw hw_stats; + + struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER]; + + int mapping[C8SECTPFEI_MAXCHANNEL]; + + struct mutex lock; + + struct timer_list timer; /* timer interrupts for outputs */ + + void __iomem *io; + void __iomem *sram; + + unsigned long sram_size; + + struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN]; + + struct clk *c8sectpfeclk; + int nima_rst_gpio; + int nimb_rst_gpio; + + int idle_irq; + int error_irq; + + int global_feed_count; +}; + +/* C8SECTPFE SYS Regs list */ + +#define SYS_INPUT_ERR_STATUS 0x0 +#define SYS_OTHER_ERR_STATUS 0x8 +#define SYS_INPUT_ERR_MASK 0x10 +#define SYS_OTHER_ERR_MASK 0x18 +#define SYS_DMA_ROUTE 0x20 +#define SYS_INPUT_CLKEN 0x30 +#define IBENABLE_MASK 0x7F + +#define SYS_OTHER_CLKEN 0x38 +#define TSDMAENABLE BIT(1) +#define MEMDMAENABLE BIT(0) + +#define SYS_CFG_NUM_IB 0x200 +#define SYS_CFG_NUM_MIB 0x204 +#define SYS_CFG_NUM_SWTS 0x208 +#define SYS_CFG_NUM_TSOUT 0x20C +#define SYS_CFG_NUM_CCSC 0x210 +#define SYS_CFG_NUM_RAM 0x214 +#define SYS_CFG_NUM_TP 0x218 + +/* Input Block Regs */ + +#define C8SECTPFE_INPUTBLK_OFFSET 0x1000 +#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET) + +#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00) +#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7) +#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6) +#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5) +#define C8SECTPFE_INVERT_TSCLK BIT(4) +#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3) +#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2) +#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1) +#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0) + +#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04) +#define C8SECTPFE_SYNC(x) (x & 0xf) +#define C8SECTPFE_DROP(x) ((x<<4) & 0xf) +#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00) +#define C8SECTPFE_SLDENDIANNESS BIT(16) + +#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08) +#define C8SECTPFE_TAG_HEADER(x) (x << 16) +#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff) +#define C8SECTPFE_TAG_ENABLE BIT(0) + +#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C) +#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f) +#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff) +#define C8SECTPFE_PID_ENABLE BIT(31) + +#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10) + +#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14) +#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18) +#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C) +#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20) + +#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24) +#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff) +#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24) +#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28) + +#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28) +#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1) +#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2) +#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4) +#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8) +#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10) +#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf) +#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf) + +#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C) +#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0) +#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1) +#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2) +#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3) +#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4) +#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8) +#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12) + +#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30) +#define C8SECTPFE_SYS_RESET BIT(1) +#define C8SECTPFE_SYS_ENABLE BIT(0) + +/* + * Ponter record data structure required for each input block + * see Table 82 on page 167 of functional specification. + */ + +#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */ +#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */ + +/* + * TS packet size, including tag bytes added by input block, + * rounded up to the next multiple of 8 bytes. The packet size, + * including any tagging bytes and rounded up to the nearest + * multiple of 8 bytes must be less than 255 bytes. + */ +#define DMA_PRDS_PKTSIZE 0x8 +#define DMA_PRDS_TPENABLE 0xc + +#define TP0_OFFSET 0x10 +#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET) +#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4) +#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8) +#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc) + +#define DMA_PRDS_SIZE (0x20) + +#define DMA_MEMDMA_OFFSET 0x4000 +#define DMA_IMEM_OFFSET 0x0 +#define DMA_DMEM_OFFSET 0x4000 +#define DMA_CPU 0x8000 +#define DMA_PER_OFFSET 0xb000 + +#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET) +#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET) + +/* XP70 Slim core regs */ +#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0) +#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4) +#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8) +#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc) +#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20) + +/* Enable Interrupt for a IB */ +#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00) +/* Ack interrupt by setting corresponding bit */ +#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80) +#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00) +#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80) +#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80) +#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88) +#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c) +#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90) +#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8) +#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac) +#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0) +#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4) +#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc) +#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0) +#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8) +#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0) +#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8) +#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0) +#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4) +#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8) +#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec) +#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0) +#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4) +#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8) +#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc) +/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */ + +/* The following are from DMA_DMEM_BaseAddress */ +#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0) +#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4) +#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8) +#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc) +#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4) +#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10) +#define IDLEREQ BIT(31) + +#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14) + +/* Regs for PID Filter */ + +#define PIDF_OFFSET 0x2800 +#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET) +#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100) +#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108) +#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110) +#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114) + +#endif /* _C8SECTPFE_CORE_H_ */ diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c new file mode 100644 index 000000000..e9ba13db4 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c @@ -0,0 +1,271 @@ +/* + * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License 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 +#include +#include +#include +#include +#include +#include +#include + +#include "c8sectpfe-debugfs.h" + +#define dump_register(nm ...) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 fei_sys_regs[] = { + dump_register(SYS_INPUT_ERR_STATUS), + dump_register(SYS_OTHER_ERR_STATUS), + dump_register(SYS_INPUT_ERR_MASK), + dump_register(SYS_DMA_ROUTE), + dump_register(SYS_INPUT_CLKEN), + dump_register(IBENABLE_MASK), + dump_register(SYS_OTHER_CLKEN), + dump_register(SYS_CFG_NUM_IB), + dump_register(SYS_CFG_NUM_MIB), + dump_register(SYS_CFG_NUM_SWTS), + dump_register(SYS_CFG_NUM_TSOUT), + dump_register(SYS_CFG_NUM_CCSC), + dump_register(SYS_CFG_NUM_RAM), + dump_register(SYS_CFG_NUM_TP), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)), + dump_register(C8SECTPFE_IB_PID_SET(0)), + dump_register(C8SECTPFE_IB_PKT_LEN(0)), + dump_register(C8SECTPFE_IB_BUFF_STRT(0)), + dump_register(C8SECTPFE_IB_BUFF_END(0)), + dump_register(C8SECTPFE_IB_READ_PNT(0)), + dump_register(C8SECTPFE_IB_WRT_PNT(0)), + dump_register(C8SECTPFE_IB_PRI_THRLD(0)), + dump_register(C8SECTPFE_IB_STAT(0)), + dump_register(C8SECTPFE_IB_MASK(0)), + dump_register(C8SECTPFE_IB_SYS(0)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)), + dump_register(C8SECTPFE_IB_PID_SET(1)), + dump_register(C8SECTPFE_IB_PKT_LEN(1)), + dump_register(C8SECTPFE_IB_BUFF_STRT(1)), + dump_register(C8SECTPFE_IB_BUFF_END(1)), + dump_register(C8SECTPFE_IB_READ_PNT(1)), + dump_register(C8SECTPFE_IB_WRT_PNT(1)), + dump_register(C8SECTPFE_IB_PRI_THRLD(1)), + dump_register(C8SECTPFE_IB_STAT(1)), + dump_register(C8SECTPFE_IB_MASK(1)), + dump_register(C8SECTPFE_IB_SYS(1)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)), + dump_register(C8SECTPFE_IB_PID_SET(2)), + dump_register(C8SECTPFE_IB_PKT_LEN(2)), + dump_register(C8SECTPFE_IB_BUFF_STRT(2)), + dump_register(C8SECTPFE_IB_BUFF_END(2)), + dump_register(C8SECTPFE_IB_READ_PNT(2)), + dump_register(C8SECTPFE_IB_WRT_PNT(2)), + dump_register(C8SECTPFE_IB_PRI_THRLD(2)), + dump_register(C8SECTPFE_IB_STAT(2)), + dump_register(C8SECTPFE_IB_MASK(2)), + dump_register(C8SECTPFE_IB_SYS(2)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)), + dump_register(C8SECTPFE_IB_PID_SET(3)), + dump_register(C8SECTPFE_IB_PKT_LEN(3)), + dump_register(C8SECTPFE_IB_BUFF_STRT(3)), + dump_register(C8SECTPFE_IB_BUFF_END(3)), + dump_register(C8SECTPFE_IB_READ_PNT(3)), + dump_register(C8SECTPFE_IB_WRT_PNT(3)), + dump_register(C8SECTPFE_IB_PRI_THRLD(3)), + dump_register(C8SECTPFE_IB_STAT(3)), + dump_register(C8SECTPFE_IB_MASK(3)), + dump_register(C8SECTPFE_IB_SYS(3)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)), + dump_register(C8SECTPFE_IB_PID_SET(4)), + dump_register(C8SECTPFE_IB_PKT_LEN(4)), + dump_register(C8SECTPFE_IB_BUFF_STRT(4)), + dump_register(C8SECTPFE_IB_BUFF_END(4)), + dump_register(C8SECTPFE_IB_READ_PNT(4)), + dump_register(C8SECTPFE_IB_WRT_PNT(4)), + dump_register(C8SECTPFE_IB_PRI_THRLD(4)), + dump_register(C8SECTPFE_IB_STAT(4)), + dump_register(C8SECTPFE_IB_MASK(4)), + dump_register(C8SECTPFE_IB_SYS(4)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)), + dump_register(C8SECTPFE_IB_PID_SET(5)), + dump_register(C8SECTPFE_IB_PKT_LEN(5)), + dump_register(C8SECTPFE_IB_BUFF_STRT(5)), + dump_register(C8SECTPFE_IB_BUFF_END(5)), + dump_register(C8SECTPFE_IB_READ_PNT(5)), + dump_register(C8SECTPFE_IB_WRT_PNT(5)), + dump_register(C8SECTPFE_IB_PRI_THRLD(5)), + dump_register(C8SECTPFE_IB_STAT(5)), + dump_register(C8SECTPFE_IB_MASK(5)), + dump_register(C8SECTPFE_IB_SYS(5)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)), + dump_register(C8SECTPFE_IB_PID_SET(6)), + dump_register(C8SECTPFE_IB_PKT_LEN(6)), + dump_register(C8SECTPFE_IB_BUFF_STRT(6)), + dump_register(C8SECTPFE_IB_BUFF_END(6)), + dump_register(C8SECTPFE_IB_READ_PNT(6)), + dump_register(C8SECTPFE_IB_WRT_PNT(6)), + dump_register(C8SECTPFE_IB_PRI_THRLD(6)), + dump_register(C8SECTPFE_IB_STAT(6)), + dump_register(C8SECTPFE_IB_MASK(6)), + dump_register(C8SECTPFE_IB_SYS(6)), + + dump_register(DMA_CPU_ID), + dump_register(DMA_CPU_VCR), + dump_register(DMA_CPU_RUN), + dump_register(DMA_CPU_PC), + + dump_register(DMA_PER_TPn_DREQ_MASK), + dump_register(DMA_PER_TPn_DACK_SET), + dump_register(DMA_PER_TPn_DREQ), + dump_register(DMA_PER_TPn_DACK), + dump_register(DMA_PER_DREQ_MODE), + dump_register(DMA_PER_STBUS_SYNC), + dump_register(DMA_PER_STBUS_ACCESS), + dump_register(DMA_PER_STBUS_ADDRESS), + dump_register(DMA_PER_IDLE_INT), + dump_register(DMA_PER_PRIORITY), + dump_register(DMA_PER_MAX_OPCODE), + dump_register(DMA_PER_MAX_CHUNK), + dump_register(DMA_PER_PAGE_SIZE), + dump_register(DMA_PER_MBOX_STATUS), + dump_register(DMA_PER_MBOX_SET), + dump_register(DMA_PER_MBOX_CLEAR), + dump_register(DMA_PER_MBOX_MASK), + dump_register(DMA_PER_INJECT_PKT_SRC), + dump_register(DMA_PER_INJECT_PKT_DEST), + dump_register(DMA_PER_INJECT_PKT_ADDR), + dump_register(DMA_PER_INJECT_PKT), + dump_register(DMA_PER_PAT_PTR_INIT), + dump_register(DMA_PER_PAT_PTR), + dump_register(DMA_PER_SLEEP_MASK), + dump_register(DMA_PER_SLEEP_COUNTER), + + dump_register(DMA_FIRMWARE_VERSION), + dump_register(DMA_PTRREC_BASE), + dump_register(DMA_PTRREC_INPUT_OFFSET), + dump_register(DMA_ERRREC_BASE), + + dump_register(DMA_ERROR_RECORD(0)), + dump_register(DMA_ERROR_RECORD(1)), + dump_register(DMA_ERROR_RECORD(2)), + dump_register(DMA_ERROR_RECORD(3)), + dump_register(DMA_ERROR_RECORD(4)), + dump_register(DMA_ERROR_RECORD(5)), + dump_register(DMA_ERROR_RECORD(6)), + dump_register(DMA_ERROR_RECORD(7)), + dump_register(DMA_ERROR_RECORD(8)), + dump_register(DMA_ERROR_RECORD(9)), + dump_register(DMA_ERROR_RECORD(10)), + dump_register(DMA_ERROR_RECORD(11)), + dump_register(DMA_ERROR_RECORD(12)), + dump_register(DMA_ERROR_RECORD(13)), + dump_register(DMA_ERROR_RECORD(14)), + dump_register(DMA_ERROR_RECORD(15)), + dump_register(DMA_ERROR_RECORD(16)), + dump_register(DMA_ERROR_RECORD(17)), + dump_register(DMA_ERROR_RECORD(18)), + dump_register(DMA_ERROR_RECORD(19)), + dump_register(DMA_ERROR_RECORD(20)), + dump_register(DMA_ERROR_RECORD(21)), + dump_register(DMA_ERROR_RECORD(22)), + + dump_register(DMA_IDLE_REQ), + dump_register(DMA_FIRMWARE_CONFIG), + + dump_register(PIDF_BASE(0)), + dump_register(PIDF_BASE(1)), + dump_register(PIDF_BASE(2)), + dump_register(PIDF_BASE(3)), + dump_register(PIDF_BASE(4)), + dump_register(PIDF_BASE(5)), + dump_register(PIDF_BASE(6)), + dump_register(PIDF_BASE(7)), + dump_register(PIDF_BASE(8)), + dump_register(PIDF_BASE(9)), + dump_register(PIDF_BASE(10)), + dump_register(PIDF_BASE(11)), + dump_register(PIDF_BASE(12)), + dump_register(PIDF_BASE(13)), + dump_register(PIDF_BASE(14)), + dump_register(PIDF_BASE(15)), + dump_register(PIDF_BASE(16)), + dump_register(PIDF_BASE(17)), + dump_register(PIDF_BASE(18)), + dump_register(PIDF_BASE(19)), + dump_register(PIDF_BASE(20)), + dump_register(PIDF_BASE(21)), + dump_register(PIDF_BASE(22)), + dump_register(PIDF_LEAK_ENABLE), + dump_register(PIDF_LEAK_STATUS), + dump_register(PIDF_LEAK_COUNT_RESET), + dump_register(PIDF_LEAK_COUNTER), +}; + +void c8sectpfe_debugfs_init(struct c8sectpfei *fei) +{ + struct dentry *root; + struct dentry *file; + + root = debugfs_create_dir("c8sectpfe", NULL); + if (!root) + goto err; + + fei->root = root; + + fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL); + if (!fei->regset) + goto err; + + fei->regset->regs = fei_sys_regs; + fei->regset->nregs = ARRAY_SIZE(fei_sys_regs); + fei->regset->base = fei->io; + + file = debugfs_create_regset32("registers", S_IRUGO, root, + fei->regset); + if (!file) { + dev_err(fei->dev, + "%s not able to create 'registers' debugfs\n" + , __func__); + goto err; + } + + return; + +err: + debugfs_remove_recursive(root); +} + +void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) +{ + debugfs_remove_recursive(fei->root); + fei->root = NULL; +} diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h new file mode 100644 index 000000000..8af1ac137 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h @@ -0,0 +1,26 @@ +/** + * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header + * + * Copyright (c) STMicroelectronics 2015 + * + * Authors: Peter Griffin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License 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 __C8SECTPFE_DEBUG_H +#define __C8SECTPFE_DEBUG_H + +#include "c8sectpfe-core.h" + +void c8sectpfe_debugfs_init(struct c8sectpfei *); +void c8sectpfe_debugfs_exit(struct c8sectpfei *); + +#endif /* __C8SECTPFE_DEBUG_H */ diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c new file mode 100644 index 000000000..69d7fe447 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c @@ -0,0 +1,244 @@ +/* + * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author Peter Griffin + * + * 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 +#include +#include +#include +#include + +#include + +#include "c8sectpfe-common.h" +#include "c8sectpfe-core.h" +#include "c8sectpfe-dvb.h" + +#include "dvb-pll.h" +#include "lnbh24.h" +#include "stv0367.h" +#include "stv0367_priv.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "tda18212.h" + +static inline const char *dvb_card_str(unsigned int c) +{ + switch (c) { + case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1"; + case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2"; + case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1"; + case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2"; + case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA"; + case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB"; + default: return "unknown dvb frontend card"; + } +} + +static struct stv090x_config stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + .xtal = 16000000, + .address = 0x69, + + .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + + .repeater_level = STV090x_RPTLEVEL_64, + + .tuner_init = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config stv6110x_config = { + .addr = 0x60, + .refclk = 16000000, +}; + +#define NIMA 0 +#define NIMB 1 + +static struct stv0367_config stv0367_tda18212_config[] = { + { + .demod_address = 0x1c, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, { + .demod_address = 0x1d, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, { + .demod_address = 0x1e, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, +}; + +static struct tda18212_config tda18212_conf = { + .if_dvbt_6 = 4150, + .if_dvbt_7 = 4150, + .if_dvbt_8 = 4500, + .if_dvbc = 5000, +}; + +int c8sectpfe_frontend_attach(struct dvb_frontend **fe, + struct c8sectpfe *c8sectpfe, + struct channel_info *tsin, int chan_num) +{ + struct tda18212_config *tda18212; + struct stv6110x_devctl *fe2; + struct i2c_client *client; + struct i2c_board_info tda18212_info = { + .type = "tda18212", + .addr = 0x60, + }; + + if (!tsin) + return -EINVAL; + + switch (tsin->dvb_card) { + + case STV0367_TDA18212_NIMA_1: + case STV0367_TDA18212_NIMA_2: + case STV0367_TDA18212_NIMB_1: + case STV0367_TDA18212_NIMB_2: + if (tsin->dvb_card == STV0367_TDA18212_NIMA_1) + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[0], + tsin->i2c_adapter); + else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1) + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[1], + tsin->i2c_adapter); + else + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[2], + tsin->i2c_adapter); + + if (!*fe) { + dev_err(c8sectpfe->device, + "%s: stv0367ter_attach failed for NIM card %s\n" + , __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + }; + + /* + * init the demod so that i2c gate_ctrl + * to the tuner works correctly + */ + (*fe)->ops.init(*fe); + + /* Allocate the tda18212 structure */ + tda18212 = devm_kzalloc(c8sectpfe->device, + sizeof(struct tda18212_config), + GFP_KERNEL); + if (!tda18212) { + dev_err(c8sectpfe->device, + "%s: devm_kzalloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(tda18212, &tda18212_conf, + sizeof(struct tda18212_config)); + + tda18212->fe = (*fe); + + tda18212_info.platform_data = tda18212; + + /* attach tuner */ + request_module("tda18212"); + client = i2c_new_device(tsin->i2c_adapter, &tda18212_info); + if (!client || !client->dev.driver) { + dvb_frontend_detach(*fe); + return -ENODEV; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(*fe); + return -ENODEV; + } + + tsin->i2c_client = client; + + break; + + case STV0903_6110_LNB24_NIMA: + *fe = dvb_attach(stv090x_attach, &stv090x_config, + tsin->i2c_adapter, STV090x_DEMODULATOR_0); + if (!*fe) { + dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n" + "\tfor NIM card %s\n", + __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + } + + fe2 = dvb_attach(stv6110x_attach, *fe, + &stv6110x_config, tsin->i2c_adapter); + if (!fe2) { + dev_err(c8sectpfe->device, + "%s: stv6110x_attach failed for NIM card %s\n" + , __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + }; + + stv090x_config.tuner_init = fe2->tuner_init; + stv090x_config.tuner_set_mode = fe2->tuner_set_mode; + stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency; + stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency; + stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth; + stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth; + stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain; + stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain; + stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk; + stv090x_config.tuner_get_status = fe2->tuner_get_status; + + dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9); + break; + + default: + dev_err(c8sectpfe->device, + "%s: DVB frontend card %s not yet supported\n", + __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + } + + (*fe)->id = chan_num; + + dev_info(c8sectpfe->device, + "DVB frontend card %s successfully attached", + dvb_card_str(tsin->dvb_card)); + return 0; +} diff --git a/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h new file mode 100644 index 000000000..bd366dbc8 --- /dev/null +++ b/kernel/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h @@ -0,0 +1,20 @@ +/* + * c8sectpfe-common.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_DVB_H_ +#define _C8SECTPFE_DVB_H_ + +int c8sectpfe_frontend_attach(struct dvb_frontend **fe, + struct c8sectpfe *c8sectpfe, struct channel_info *tsin, + int chan_num); + +#endif diff --git a/kernel/drivers/media/platform/ti-vpe/vpe.c b/kernel/drivers/media/platform/ti-vpe/vpe.c index c44760b70..de24effd9 100644 --- a/kernel/drivers/media/platform/ti-vpe/vpe.c +++ b/kernel/drivers/media/platform/ti-vpe/vpe.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include "vpdma.h" @@ -384,8 +384,8 @@ struct vpe_ctx { unsigned int bufs_completed; /* bufs done in this batch */ struct vpe_q_data q_data[2]; /* src & dst queue data */ - struct vb2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; - struct vb2_buffer *dst_vb; + struct vb2_v4l2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; + struct vb2_v4l2_buffer *dst_vb; dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */ void *mv_buf[2]; /* virtual addrs of motion vector bufs */ @@ -988,7 +988,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) { struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; const struct vpe_port_data *p_data = &port_data[port]; - struct vb2_buffer *vb = ctx->dst_vb; + struct vb2_buffer *vb = &ctx->dst_vb->vb2_buf; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = !ctx->src_mv_buf_selector; @@ -1025,11 +1025,12 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) { struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; const struct vpe_port_data *p_data = &port_data[port]; - struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index]; + struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = ctx->src_mv_buf_selector; - int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM; + int field = vbuf->field == V4L2_FIELD_BOTTOM; int frame_width, frame_height; dma_addr_t dma_addr; u32 flags = 0; @@ -1222,8 +1223,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) struct vpe_dev *dev = (struct vpe_dev *)data; struct vpe_ctx *ctx; struct vpe_q_data *d_q_data; - struct vb2_buffer *s_vb, *d_vb; - struct v4l2_buffer *s_buf, *d_buf; + struct vb2_v4l2_buffer *s_vb, *d_vb; unsigned long flags; u32 irqst0, irqst1; @@ -1286,20 +1286,18 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) s_vb = ctx->src_vbs[0]; d_vb = ctx->dst_vb; - s_buf = &s_vb->v4l2_buf; - d_buf = &d_vb->v4l2_buf; - d_buf->flags = s_buf->flags; + d_vb->flags = s_vb->flags; + d_vb->timestamp = s_vb->timestamp; - d_buf->timestamp = s_buf->timestamp; - if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) - d_buf->timecode = s_buf->timecode; + if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE) + d_vb->timecode = s_vb->timecode; - d_buf->sequence = ctx->sequence; + d_vb->sequence = ctx->sequence; d_q_data = &ctx->q_data[Q_DATA_DST]; if (d_q_data->flags & Q_DATA_INTERLACED) { - d_buf->field = ctx->field; + d_vb->field = ctx->field; if (ctx->field == V4L2_FIELD_BOTTOM) { ctx->sequence++; ctx->field = V4L2_FIELD_TOP; @@ -1308,7 +1306,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) ctx->field = V4L2_FIELD_BOTTOM; } } else { - d_buf->field = V4L2_FIELD_NONE; + d_vb->field = V4L2_FIELD_NONE; ctx->sequence++; } @@ -1798,7 +1796,7 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { * Queue operations */ static int vpe_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -1825,6 +1823,7 @@ static int vpe_queue_setup(struct vb2_queue *vq, static int vpe_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vpe_q_data *q_data; int i, num_planes; @@ -1836,10 +1835,10 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (!(q_data->flags & Q_DATA_INTERLACED)) { - vb->v4l2_buf.field = V4L2_FIELD_NONE; + vbuf->field = V4L2_FIELD_NONE; } else { - if (vb->v4l2_buf.field != V4L2_FIELD_TOP && - vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) + if (vbuf->field != V4L2_FIELD_TOP && + vbuf->field != V4L2_FIELD_BOTTOM) return -EINVAL; } } @@ -1862,9 +1861,10 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) static void vpe_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static int vpe_start_streaming(struct vb2_queue *q, unsigned int count) diff --git a/kernel/drivers/media/platform/via-camera.c b/kernel/drivers/media/platform/via-camera.c index 678ed9f35..32e4ff46d 100644 --- a/kernel/drivers/media/platform/via-camera.c +++ b/kernel/drivers/media/platform/via-camera.c @@ -249,13 +249,15 @@ static int viacam_set_flip(struct via_camera *cam) */ static int viacam_configure_sensor(struct via_camera *cam) { - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; - v4l2_fill_mbus_format(&mbus_fmt, &cam->sensor_format, cam->mbus_code); + v4l2_fill_mbus_format(&format.format, &cam->sensor_format, cam->mbus_code); ret = sensor_call(cam, core, init, 0); if (ret == 0) - ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt); + ret = sensor_call(cam, pad, set_fmt, NULL, &format); /* * OV7670 does weird things if flip is set *before* format... */ @@ -903,14 +905,17 @@ static int viacam_do_try_fmt(struct via_camera *cam, struct v4l2_pix_format *upix, struct v4l2_pix_format *spix) { int ret; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; struct via_format *f = via_find_format(upix->pixelformat); upix->pixelformat = f->pixelformat; viacam_fmt_pre(upix, spix); - v4l2_fill_mbus_format(&mbus_fmt, spix, f->mbus_code); - ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(spix, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, spix, f->mbus_code); + ret = sensor_call(cam, pad, set_fmt, &pad_cfg, &format); + v4l2_fill_pix_format(spix, &format.format); viacam_fmt_post(upix, spix); return ret; } diff --git a/kernel/drivers/media/platform/vim2m.c b/kernel/drivers/media/platform/vim2m.c index 4d6b4cc57..e18fb9f9e 100644 --- a/kernel/drivers/media/platform/vim2m.c +++ b/kernel/drivers/media/platform/vim2m.c @@ -80,7 +80,6 @@ static struct platform_device vim2m_pdev = { }; struct vim2m_fmt { - char *name; u32 fourcc; int depth; /* Types the format can be used for */ @@ -89,14 +88,12 @@ struct vim2m_fmt { static struct vim2m_fmt formats[] = { { - .name = "RGB565 (BE)", .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ .depth = 16, /* Both capture and output format */ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, }, { - .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, /* Output-only format */ @@ -200,8 +197,8 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, static int device_process(struct vim2m_ctx *ctx, - struct vb2_buffer *in_vb, - struct vb2_buffer *out_vb) + struct vb2_v4l2_buffer *in_vb, + struct vb2_v4l2_buffer *out_vb) { struct vim2m_dev *dev = ctx->dev; struct vim2m_q_data *q_data; @@ -216,15 +213,16 @@ static int device_process(struct vim2m_ctx *ctx, height = q_data->height; bytesperline = (q_data->width * q_data->fmt->depth) >> 3; - p_in = vb2_plane_vaddr(in_vb, 0); - p_out = vb2_plane_vaddr(out_vb, 0); + p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); + p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); if (!p_in || !p_out) { v4l2_err(&dev->v4l2_dev, "Acquiring kernel pointers to buffers failed\n"); return -EFAULT; } - if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) { + if (vb2_plane_size(&in_vb->vb2_buf, 0) > + vb2_plane_size(&out_vb->vb2_buf, 0)) { v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); return -EINVAL; } @@ -234,16 +232,15 @@ static int device_process(struct vim2m_ctx *ctx, bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; - out_vb->v4l2_buf.sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; - in_vb->v4l2_buf.sequence = q_data->sequence++; - memcpy(&out_vb->v4l2_buf.timestamp, - &in_vb->v4l2_buf.timestamp, - sizeof(struct timeval)); - if (in_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE) - memcpy(&out_vb->v4l2_buf.timecode, &in_vb->v4l2_buf.timecode, - sizeof(struct v4l2_timecode)); - out_vb->v4l2_buf.field = in_vb->v4l2_buf.field; - out_vb->v4l2_buf.flags = in_vb->v4l2_buf.flags & + out_vb->sequence = + get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; + in_vb->sequence = q_data->sequence++; + out_vb->timestamp = in_vb->timestamp; + + if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE) + out_vb->timecode = in_vb->timecode; + out_vb->field = in_vb->field; + out_vb->flags = in_vb->flags & (V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | @@ -377,7 +374,7 @@ static void device_run(void *priv) { struct vim2m_ctx *ctx = priv; struct vim2m_dev *dev = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -392,7 +389,7 @@ static void device_isr(unsigned long priv) { struct vim2m_dev *vim2m_dev = (struct vim2m_dev *)priv; struct vim2m_ctx *curr_ctx; - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_v4l2_buffer *src_vb, *dst_vb; unsigned long flags; curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev); @@ -458,7 +455,6 @@ static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) if (i < NUM_FORMATS) { /* Format found */ fmt = &formats[i]; - strncpy(f->description, fmt->name, sizeof(f->description) - 1); f->pixelformat = fmt->fourcc; return 0; } @@ -697,6 +693,8 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, .vidioc_streamon = v4l2_m2m_ioctl_streamon, @@ -712,10 +710,11 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { */ static int vim2m_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); struct vim2m_q_data *q_data; unsigned int size, count = *nbuffers; @@ -724,6 +723,12 @@ static int vim2m_queue_setup(struct vb2_queue *vq, size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + if (fmt) { + if (fmt->fmt.pix.sizeimage < size) + return -EINVAL; + size = fmt->fmt.pix.sizeimage; + } + while (size * count > MEM2MEM_VID_MEM_LIMIT) (count)--; @@ -743,6 +748,7 @@ static int vim2m_queue_setup(struct vb2_queue *vq, static int vim2m_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; @@ -750,9 +756,9 @@ static int vim2m_buf_prepare(struct vb2_buffer *vb) q_data = get_q_data(ctx, vb->vb2_queue->type); if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vb->v4l2_buf.field == V4L2_FIELD_ANY) - vb->v4l2_buf.field = V4L2_FIELD_NONE; - if (vb->v4l2_buf.field != V4L2_FIELD_NONE) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { dprintk(ctx->dev, "%s field isn't supported\n", __func__); return -EINVAL; @@ -772,9 +778,10 @@ static int vim2m_buf_prepare(struct vb2_buffer *vb) static void vim2m_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } static int vim2m_start_streaming(struct vb2_queue *q, unsigned count) @@ -789,18 +796,18 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned count) static void vim2m_stop_streaming(struct vb2_queue *q) { struct vim2m_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; unsigned long flags; for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type)) - vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); else - vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (vb == NULL) + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (vbuf == NULL) return; spin_lock_irqsave(&ctx->dev->irqlock, flags); - v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&ctx->dev->irqlock, flags); } } diff --git a/kernel/drivers/media/platform/vivid/Kconfig b/kernel/drivers/media/platform/vivid/Kconfig index c3090932f..0885e93ad 100644 --- a/kernel/drivers/media/platform/vivid/Kconfig +++ b/kernel/drivers/media/platform/vivid/Kconfig @@ -20,3 +20,11 @@ config VIDEO_VIVID Say Y here if you want to test video apps or debug V4L devices. When in doubt, say N. + +config VIDEO_VIVID_MAX_DEVS + int "Maximum number of devices" + depends on VIDEO_VIVID + default "64" + ---help--- + This allows you to specify the maximum number of devices supported + by the vivid driver. diff --git a/kernel/drivers/media/platform/vivid/vivid-core.c b/kernel/drivers/media/platform/vivid/vivid-core.c index d33f16495..ec125becb 100644 --- a/kernel/drivers/media/platform/vivid/vivid-core.c +++ b/kernel/drivers/media/platform/vivid/vivid-core.c @@ -51,7 +51,7 @@ #define VIVID_MODULE_NAME "vivid" /* The maximum number of vivid devices */ -#define VIVID_MAX_DEVS 64 +#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS MODULE_DESCRIPTION("Virtual Video Test Driver"); MODULE_AUTHOR("Hans Verkuil"); @@ -392,6 +392,17 @@ static int vidioc_s_parm(struct file *file, void *fh, return vivid_vid_out_g_parm(file, fh, parm); } +static int vidioc_log_status(struct file *file, void *fh) +{ + struct vivid_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + v4l2_ctrl_log_status(file, fh); + if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_GRABBER) + tpg_log_status(&dev->tpg); + return 0; +} + static ssize_t vivid_radio_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { @@ -548,8 +559,8 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_enum_fmt_sdr_cap = vidioc_enum_fmt_sdr_cap, .vidioc_g_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, - .vidioc_s_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = vidioc_try_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = vidioc_s_fmt_sdr_cap, .vidioc_overlay = vidioc_overlay, .vidioc_enum_framesizes = vidioc_enum_framesizes, @@ -610,7 +621,7 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_g_edid = vidioc_g_edid, .vidioc_s_edid = vidioc_s_edid, - .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = vidioc_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -966,6 +977,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; dev->sdr_adc_freq = 300000; dev->sdr_fm_freq = 50000000; + dev->sdr_pixelformat = V4L2_SDR_FMT_CU8; + dev->sdr_buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; + dev->edid_max_blocks = dev->edid_blocks = 2; memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); ktime_get_ts(&dev->radio_rds_init_ts); @@ -1327,8 +1341,11 @@ static int vivid_remove(struct platform_device *pdev) struct vivid_dev *dev; unsigned i; - for (i = 0; vivid_devs[i]; i++) { + + for (i = 0; i < n_devs; i++) { dev = vivid_devs[i]; + if (!dev) + continue; if (dev->has_vid_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", diff --git a/kernel/drivers/media/platform/vivid/vivid-core.h b/kernel/drivers/media/platform/vivid/vivid-core.h index 9e15aee9a..55b304a70 100644 --- a/kernel/drivers/media/platform/vivid/vivid-core.h +++ b/kernel/drivers/media/platform/vivid/vivid-core.h @@ -21,7 +21,7 @@ #define _VIVID_CORE_H_ #include -#include +#include #include #include #include @@ -77,7 +77,6 @@ extern const struct v4l2_rect vivid_max_rect; extern unsigned vivid_debug; struct vivid_fmt { - const char *name; u32 fourcc; /* v4l2 format id */ bool is_yuv; bool can_do_overlay; @@ -94,7 +93,7 @@ extern struct vivid_fmt vivid_formats[]; /* buffer for one video frame */ struct vivid_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -124,6 +123,7 @@ enum vivid_colorspace { VIVID_CS_SRGB, VIVID_CS_ADOBERGB, VIVID_CS_2020, + VIVID_CS_DCI_P3, VIVID_CS_240M, VIVID_CS_SYS_M, VIVID_CS_SYS_BG, @@ -140,7 +140,7 @@ struct vivid_dev { struct v4l2_ctrl_handler ctrl_hdl_user_aud; struct v4l2_ctrl_handler ctrl_hdl_streaming; struct v4l2_ctrl_handler ctrl_hdl_sdtv_cap; - struct v4l2_ctrl_handler ctrl_hdl_loop_out; + struct v4l2_ctrl_handler ctrl_hdl_loop_cap; struct video_device vid_cap_dev; struct v4l2_ctrl_handler ctrl_hdl_vid_cap; struct video_device vid_out_dev; @@ -333,6 +333,7 @@ struct vivid_dev { u32 colorspace_out; u32 ycbcr_enc_out; u32 quantization_out; + u32 xfer_func_out; u32 service_set_out; unsigned bytesperline_out[TPG_MAX_PLANES]; unsigned tv_field_out; @@ -447,8 +448,11 @@ struct vivid_dev { /* SDR capture */ struct vb2_queue vb_sdr_cap_q; struct list_head sdr_cap_active; + u32 sdr_pixelformat; /* v4l2 format id */ + unsigned sdr_buffersize; unsigned sdr_adc_freq; unsigned sdr_fm_freq; + unsigned sdr_fm_deviation; int sdr_fixp_src_phase; int sdr_fixp_mod_phase; diff --git a/kernel/drivers/media/platform/vivid/vivid-ctrls.c b/kernel/drivers/media/platform/vivid/vivid-ctrls.c index 2b9070098..f41ac0b01 100644 --- a/kernel/drivers/media/platform/vivid/vivid-ctrls.c +++ b/kernel/drivers/media/platform/vivid/vivid-ctrls.c @@ -62,21 +62,22 @@ #define VIVID_CID_DV_TIMINGS_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 23) #define VIVID_CID_TSTAMP_SRC (VIVID_CID_VIVID_BASE + 24) #define VIVID_CID_COLORSPACE (VIVID_CID_VIVID_BASE + 25) -#define VIVID_CID_YCBCR_ENC (VIVID_CID_VIVID_BASE + 26) -#define VIVID_CID_QUANTIZATION (VIVID_CID_VIVID_BASE + 27) -#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 28) -#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 29) -#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 30) -#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 31) -#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 32) -#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 33) -#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 34) -#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 35) -#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 36) -#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 37) -#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 38) -#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 39) -#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 40) +#define VIVID_CID_XFER_FUNC (VIVID_CID_VIVID_BASE + 26) +#define VIVID_CID_YCBCR_ENC (VIVID_CID_VIVID_BASE + 27) +#define VIVID_CID_QUANTIZATION (VIVID_CID_VIVID_BASE + 28) +#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 29) +#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 30) +#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 31) +#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 32) +#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 33) +#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34) +#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35) +#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36) +#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37) +#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38) +#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39) +#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40) +#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41) #define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) #define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) @@ -98,6 +99,7 @@ #define VIVID_CID_RADIO_TX_RDS_BLOCKIO (VIVID_CID_VIVID_BASE + 94) +#define VIVID_CID_SDR_CAP_FM_DEVIATION (VIVID_CID_VIVID_BASE + 110) /* General User Controls */ @@ -341,6 +343,7 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_COLORSPACE_SRGB, V4L2_COLORSPACE_ADOBERGB, V4L2_COLORSPACE_BT2020, + V4L2_COLORSPACE_DCI_P3, V4L2_COLORSPACE_SMPTE240M, V4L2_COLORSPACE_470_SYSTEM_M, V4L2_COLORSPACE_470_SYSTEM_BG, @@ -360,6 +363,13 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) vivid_send_source_change(dev, HDMI); vivid_send_source_change(dev, WEBCAM); break; + case VIVID_CID_XFER_FUNC: + tpg_s_xfer_func(&dev->tpg, ctrl->val); + vivid_send_source_change(dev, TV); + vivid_send_source_change(dev, SVID); + vivid_send_source_change(dev, HDMI); + vivid_send_source_change(dev, WEBCAM); + break; case VIVID_CID_YCBCR_ENC: tpg_s_ycbcr_enc(&dev->tpg, ctrl->val); vivid_send_source_change(dev, TV); @@ -540,7 +550,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_osd_mode = { .id = VIVID_CID_OSD_TEXT_MODE, .name = "OSD Text Mode", .type = V4L2_CTRL_TYPE_MENU, - .max = 2, + .max = ARRAY_SIZE(vivid_ctrl_osd_mode_strings) - 2, .qmenu = vivid_ctrl_osd_mode_strings, }; @@ -632,7 +642,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_tstamp_src = { .id = VIVID_CID_TSTAMP_SRC, .name = "Timestamp Source", .type = V4L2_CTRL_TYPE_MENU, - .max = 1, + .max = ARRAY_SIZE(vivid_ctrl_tstamp_src_strings) - 2, .qmenu = vivid_ctrl_tstamp_src_strings, }; @@ -693,6 +703,7 @@ static const char * const vivid_ctrl_colorspace_strings[] = { "sRGB", "AdobeRGB", "BT.2020", + "DCI-P3", "SMPTE 240M", "470 System M", "470 System BG", @@ -704,11 +715,32 @@ static const struct v4l2_ctrl_config vivid_ctrl_colorspace = { .id = VIVID_CID_COLORSPACE, .name = "Colorspace", .type = V4L2_CTRL_TYPE_MENU, - .max = 7, + .max = ARRAY_SIZE(vivid_ctrl_colorspace_strings) - 2, .def = 2, .qmenu = vivid_ctrl_colorspace_strings, }; +static const char * const vivid_ctrl_xfer_func_strings[] = { + "Default", + "Rec. 709", + "sRGB", + "AdobeRGB", + "SMPTE 240M", + "None", + "DCI-P3", + "SMPTE 2084", + NULL, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_xfer_func = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_XFER_FUNC, + .name = "Transfer Function", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(vivid_ctrl_xfer_func_strings) - 2, + .qmenu = vivid_ctrl_xfer_func_strings, +}; + static const char * const vivid_ctrl_ycbcr_enc_strings[] = { "Default", "ITU-R 601", @@ -727,7 +759,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_ycbcr_enc = { .id = VIVID_CID_YCBCR_ENC, .name = "Y'CbCr Encoding", .type = V4L2_CTRL_TYPE_MENU, - .max = 8, + .max = ARRAY_SIZE(vivid_ctrl_ycbcr_enc_strings) - 2, .qmenu = vivid_ctrl_ycbcr_enc_strings, }; @@ -743,7 +775,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_quantization = { .id = VIVID_CID_QUANTIZATION, .name = "Quantization", .type = V4L2_CTRL_TYPE_MENU, - .max = 2, + .max = ARRAY_SIZE(vivid_ctrl_quantization_strings) - 2, .qmenu = vivid_ctrl_quantization_strings, }; @@ -766,6 +798,37 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = { }; +/* Video Loop Control */ + +static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap); + + switch (ctrl->id) { + case VIVID_CID_LOOP_VIDEO: + dev->loop_video = ctrl->val; + vivid_update_quality(dev); + vivid_send_source_change(dev, SVID); + vivid_send_source_change(dev, HDMI); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = { + .s_ctrl = vivid_loop_cap_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { + .ops = &vivid_loop_cap_ctrl_ops, + .id = VIVID_CID_LOOP_VIDEO, + .name = "Loop Video", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + + /* VBI Capture Control */ static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1030,7 +1093,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_std_signal_mode = { .id = VIVID_CID_STD_SIGNAL_MODE, .name = "Standard Signal Mode", .type = V4L2_CTRL_TYPE_MENU, - .max = 5, + .max = ARRAY_SIZE(vivid_ctrl_std_signal_mode_strings) - 2, .menu_skip_mask = 1 << 3, .qmenu = vivid_ctrl_std_signal_mode_strings, }; @@ -1199,35 +1262,33 @@ static const struct v4l2_ctrl_config vivid_ctrl_radio_tx_rds_blockio = { }; +/* SDR Capture Controls */ -/* Video Loop Control */ - -static int vivid_loop_out_s_ctrl(struct v4l2_ctrl *ctrl) +static int vivid_sdr_cap_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_out); + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_sdr_cap); switch (ctrl->id) { - case VIVID_CID_LOOP_VIDEO: - dev->loop_video = ctrl->val; - vivid_update_quality(dev); - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); + case VIVID_CID_SDR_CAP_FM_DEVIATION: + dev->sdr_fm_deviation = ctrl->val; break; } return 0; } -static const struct v4l2_ctrl_ops vivid_loop_out_ctrl_ops = { - .s_ctrl = vivid_loop_out_s_ctrl, +static const struct v4l2_ctrl_ops vivid_sdr_cap_ctrl_ops = { + .s_ctrl = vivid_sdr_cap_s_ctrl, }; -static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { - .ops = &vivid_loop_out_ctrl_ops, - .id = VIVID_CID_LOOP_VIDEO, - .name = "Loop Video", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, +static const struct v4l2_ctrl_config vivid_ctrl_sdr_cap_fm_deviation = { + .ops = &vivid_sdr_cap_ctrl_ops, + .id = VIVID_CID_SDR_CAP_FM_DEVIATION, + .name = "FM Deviation", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 100, + .max = 200000, + .def = 75000, + .step = 1, }; @@ -1248,7 +1309,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, struct v4l2_ctrl_handler *hdl_user_aud = &dev->ctrl_hdl_user_aud; struct v4l2_ctrl_handler *hdl_streaming = &dev->ctrl_hdl_streaming; struct v4l2_ctrl_handler *hdl_sdtv_cap = &dev->ctrl_hdl_sdtv_cap; - struct v4l2_ctrl_handler *hdl_loop_out = &dev->ctrl_hdl_loop_out; + struct v4l2_ctrl_handler *hdl_loop_cap = &dev->ctrl_hdl_loop_cap; struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap; struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out; struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap; @@ -1274,8 +1335,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_streaming, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_sdtv_cap, 2); v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL); - v4l2_ctrl_handler_init(hdl_loop_out, 1); - v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_loop_cap, 1); + v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_vid_cap, 55); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_vid_out, 26); @@ -1288,7 +1349,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_radio_tx, 17); v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL); - v4l2_ctrl_handler_init(hdl_sdr_cap, 18); + v4l2_ctrl_handler_init(hdl_sdr_cap, 19); v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL); /* User Controls */ @@ -1365,6 +1426,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_tstamp_src, NULL); dev->colorspace = v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_colorspace, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_xfer_func, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_ycbcr_enc, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_quantization, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_alpha_mode, NULL); @@ -1445,7 +1507,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, } if ((dev->has_vid_cap && dev->has_vid_out) || (dev->has_vbi_cap && dev->has_vbi_out)) - v4l2_ctrl_new_custom(hdl_loop_out, &vivid_ctrl_loop_video, NULL); + v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL); if (dev->has_fb) v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_clear_fb, NULL); @@ -1518,6 +1580,10 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, &vivid_radio_tx_ctrl_ops, V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1); } + if (dev->has_sdr_cap) { + v4l2_ctrl_new_custom(hdl_sdr_cap, + &vivid_ctrl_sdr_cap_fm_deviation, NULL); + } if (hdl_user_gen->error) return hdl_user_gen->error; if (hdl_user_vid->error) @@ -1528,8 +1594,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, return hdl_streaming->error; if (hdl_sdr_cap->error) return hdl_sdr_cap->error; - if (hdl_loop_out->error) - return hdl_loop_out->error; + if (hdl_loop_cap->error) + return hdl_loop_cap->error; if (dev->autogain) v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true); @@ -1540,6 +1606,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL); v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL); v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL); + v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL); if (hdl_vid_cap->error) return hdl_vid_cap->error; dev->vid_cap_dev.ctrl_handler = hdl_vid_cap; @@ -1548,7 +1615,6 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL); v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL); v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL); - v4l2_ctrl_add_handler(hdl_vid_out, hdl_loop_out, NULL); if (hdl_vid_out->error) return hdl_vid_out->error; dev->vid_out_dev.ctrl_handler = hdl_vid_out; @@ -1557,6 +1623,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL); v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL); v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL); + v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL); if (hdl_vbi_cap->error) return hdl_vbi_cap->error; dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap; @@ -1564,7 +1631,6 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, if (dev->has_vbi_out) { v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL); v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL); - v4l2_ctrl_add_handler(hdl_vbi_out, hdl_loop_out, NULL); if (hdl_vbi_out->error) return hdl_vbi_out->error; dev->vbi_out_dev.ctrl_handler = hdl_vbi_out; @@ -1607,5 +1673,5 @@ void vivid_free_controls(struct vivid_dev *dev) v4l2_ctrl_handler_free(&dev->ctrl_hdl_user_aud); v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming); v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap); - v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_out); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap); } diff --git a/kernel/drivers/media/platform/vivid/vivid-kthread-cap.c b/kernel/drivers/media/platform/vivid/vivid-kthread-cap.c index 1727f5453..83cc6d3b4 100644 --- a/kernel/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/kernel/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -236,8 +236,8 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, void *vbuf; if (p == 0 || tpg_g_buffers(tpg) > 1) - return vb2_plane_vaddr(&buf->vb, p); - vbuf = vb2_plane_vaddr(&buf->vb, 0); + return vb2_plane_vaddr(&buf->vb.vb2_buf, p); + vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); for (i = 0; i < p; i++) vbuf += bpl[i] * h / tpg->vdownsampling[i]; return vbuf; @@ -246,7 +246,7 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) { - bool blank = dev->must_blank[vid_cap_buf->vb.v4l2_buf.index]; + bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index]; struct tpg_data *tpg = &dev->tpg; struct vivid_buffer *vid_out_buf = NULL; unsigned vdiv = dev->fmt_out->vdownsampling[p]; @@ -283,12 +283,12 @@ static int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, u8 *vcapbuf, if (vid_out_buf == NULL) return -ENODATA; - vid_cap_buf->vb.v4l2_buf.field = vid_out_buf->vb.v4l2_buf.field; + vid_cap_buf->vb.field = vid_out_buf->vb.field; voutbuf = plane_vaddr(tpg, vid_out_buf, p, dev->bytesperline_out, dev->fmt_out_rect.height); if (p < dev->fmt_out->buffers) - voutbuf += vid_out_buf->vb.v4l2_planes[p].data_offset; + voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset; voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) + (dev->loop_vid_out.top / vdiv) * stride_out; vcapbuf += tpg_hdiv(tpg, p, dev->compose_cap.left) + @@ -429,17 +429,19 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) bool is_loop = false; if (dev->loop_video && dev->can_loop_video && - ((vivid_is_svid_cap(dev) && !VIVID_INVALID_SIGNAL(dev->std_signal_mode)) || - (vivid_is_hdmi_cap(dev) && !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)))) + ((vivid_is_svid_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->std_signal_mode)) || + (vivid_is_hdmi_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)))) is_loop = true; - buf->vb.v4l2_buf.sequence = dev->vid_cap_seq_count; + buf->vb.sequence = dev->vid_cap_seq_count; /* * Take the timestamp now if the timestamp source is set to * "Start of Exposure". */ if (dev->tstamp_src_is_soe) - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.timestamp); if (dev->field_cap == V4L2_FIELD_ALTERNATE) { /* * 60 Hz standards start with the bottom field, 50 Hz standards @@ -447,19 +449,19 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) * then the field is TOP for 50 Hz and BOTTOM for 60 Hz * standards. */ - buf->vb.v4l2_buf.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ? + buf->vb.field = ((dev->vid_cap_seq_count & 1) ^ is_60hz) ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; /* * The sequence counter counts frames, not fields. So divide * by two. */ - buf->vb.v4l2_buf.sequence /= 2; + buf->vb.sequence /= 2; } else { - buf->vb.v4l2_buf.field = dev->field_cap; + buf->vb.field = dev->field_cap; } - tpg_s_field(tpg, buf->vb.v4l2_buf.field, + tpg_s_field(tpg, buf->vb.field, dev->field_cap == V4L2_FIELD_ALTERNATE); - tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.v4l2_buf.index]); + tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]); vivid_precalc_copy_rects(dev); @@ -479,13 +481,16 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) } tpg_calc_text_basep(tpg, basep, p, vbuf); if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) - tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf); + tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), + p, vbuf); } - dev->must_blank[buf->vb.v4l2_buf.index] = false; + dev->must_blank[buf->vb.vb2_buf.index] = false; /* Updates stream time, only update at the start of a new frame. */ - if (dev->field_cap != V4L2_FIELD_ALTERNATE || (buf->vb.v4l2_buf.sequence & 1) == 0) - dev->ms_vid_cap = jiffies_to_msecs(jiffies - dev->jiffies_vid_cap); + if (dev->field_cap != V4L2_FIELD_ALTERNATE || + (buf->vb.sequence & 1) == 0) + dev->ms_vid_cap = + jiffies_to_msecs(jiffies - dev->jiffies_vid_cap); ms = dev->ms_vid_cap; if (dev->osd_mode <= 1) { @@ -494,9 +499,9 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) (ms / (60 * 1000)) % 60, (ms / 1000) % 60, ms % 1000, - buf->vb.v4l2_buf.sequence, + buf->vb.sequence, (dev->field_cap == V4L2_FIELD_ALTERNATE) ? - (buf->vb.v4l2_buf.field == V4L2_FIELD_TOP ? + (buf->vb.field == V4L2_FIELD_TOP ? " top" : " bottom") : ""); tpg_gen_text(tpg, basep, line++ * line_height, 16, str); } @@ -553,8 +558,8 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) * the timestamp now. */ if (!dev->tstamp_src_is_soe) - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.timestamp.tv_sec += dev->time_wrap_offset; } /* @@ -600,7 +605,7 @@ static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf) struct tpg_data *tpg = &dev->tpg; unsigned pixsize = tpg_g_twopixelsize(tpg, 0) / 2; void *vbase = dev->fb_vbase_cap; - void *vbuf = vb2_plane_vaddr(&buf->vb, 0); + void *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); unsigned img_width = dev->compose_cap.width; unsigned img_height = dev->compose_cap.height; unsigned stride = tpg->bytesperline[0]; @@ -616,7 +621,7 @@ static void vivid_overlay(struct vivid_dev *dev, struct vivid_buffer *buf) return; if ((dev->overlay_cap_field == V4L2_FIELD_TOP || dev->overlay_cap_field == V4L2_FIELD_BOTTOM) && - dev->overlay_cap_field != buf->vb.v4l2_buf.field) + dev->overlay_cap_field != buf->vb.field) return; vbuf += dev->compose_cap.left * pixsize + dev->compose_cap.top * stride; @@ -699,17 +704,17 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs) /* Fill buffer */ vivid_fillbuff(dev, vid_cap_buf); dprintk(dev, 1, "filled buffer %d\n", - vid_cap_buf->vb.v4l2_buf.index); + vid_cap_buf->vb.vb2_buf.index); /* Handle overlay */ if (dev->overlay_cap_owner && dev->fb_cap.base && - dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc) + dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc) vivid_overlay(dev, vid_cap_buf); - vb2_buffer_done(&vid_cap_buf->vb, dev->dqbuf_error ? + vb2_buffer_done(&vid_cap_buf->vb.vb2_buf, dev->dqbuf_error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); dprintk(dev, 2, "vid_cap buffer %d done\n", - vid_cap_buf->vb.v4l2_buf.index); + vid_cap_buf->vb.vb2_buf.index); } if (vbi_cap_buf) { @@ -717,10 +722,10 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs) vivid_sliced_vbi_cap_process(dev, vbi_cap_buf); else vivid_raw_vbi_cap_process(dev, vbi_cap_buf); - vb2_buffer_done(&vbi_cap_buf->vb, dev->dqbuf_error ? + vb2_buffer_done(&vbi_cap_buf->vb.vb2_buf, dev->dqbuf_error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); dprintk(dev, 2, "vbi_cap %d done\n", - vbi_cap_buf->vb.v4l2_buf.index); + vbi_cap_buf->vb.vb2_buf.index); } dev->dqbuf_error = false; @@ -884,9 +889,9 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) buf = list_entry(dev->vid_cap_active.next, struct vivid_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(dev, 2, "vid_cap buffer %d done\n", - buf->vb.v4l2_buf.index); + buf->vb.vb2_buf.index); } } @@ -897,9 +902,9 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) buf = list_entry(dev->vbi_cap_active.next, struct vivid_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(dev, 2, "vbi_cap buffer %d done\n", - buf->vb.v4l2_buf.index); + buf->vb.vb2_buf.index); } } diff --git a/kernel/drivers/media/platform/vivid/vivid-kthread-out.c b/kernel/drivers/media/platform/vivid/vivid-kthread-out.c index d9f36ccd7..c2c46dcdb 100644 --- a/kernel/drivers/media/platform/vivid/vivid-kthread-out.c +++ b/kernel/drivers/media/platform/vivid/vivid-kthread-out.c @@ -87,33 +87,33 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev) return; if (vid_out_buf) { - vid_out_buf->vb.v4l2_buf.sequence = dev->vid_out_seq_count; + vid_out_buf->vb.sequence = dev->vid_out_seq_count; if (dev->field_out == V4L2_FIELD_ALTERNATE) { /* - * The sequence counter counts frames, not fields. So divide - * by two. + * The sequence counter counts frames, not fields. + * So divide by two. */ - vid_out_buf->vb.v4l2_buf.sequence /= 2; + vid_out_buf->vb.sequence /= 2; } - v4l2_get_timestamp(&vid_out_buf->vb.v4l2_buf.timestamp); - vid_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; - vb2_buffer_done(&vid_out_buf->vb, dev->dqbuf_error ? + v4l2_get_timestamp(&vid_out_buf->vb.timestamp); + vid_out_buf->vb.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&vid_out_buf->vb.vb2_buf, dev->dqbuf_error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); dprintk(dev, 2, "vid_out buffer %d done\n", - vid_out_buf->vb.v4l2_buf.index); + vid_out_buf->vb.vb2_buf.index); } if (vbi_out_buf) { if (dev->stream_sliced_vbi_out) vivid_sliced_vbi_out_process(dev, vbi_out_buf); - vbi_out_buf->vb.v4l2_buf.sequence = dev->vbi_out_seq_count; - v4l2_get_timestamp(&vbi_out_buf->vb.v4l2_buf.timestamp); - vbi_out_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; - vb2_buffer_done(&vbi_out_buf->vb, dev->dqbuf_error ? + vbi_out_buf->vb.sequence = dev->vbi_out_seq_count; + v4l2_get_timestamp(&vbi_out_buf->vb.timestamp); + vbi_out_buf->vb.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&vbi_out_buf->vb.vb2_buf, dev->dqbuf_error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); dprintk(dev, 2, "vbi_out buffer %d done\n", - vbi_out_buf->vb.v4l2_buf.index); + vbi_out_buf->vb.vb2_buf.index); } dev->dqbuf_error = false; } @@ -274,9 +274,9 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) buf = list_entry(dev->vid_out_active.next, struct vivid_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(dev, 2, "vid_out buffer %d done\n", - buf->vb.v4l2_buf.index); + buf->vb.vb2_buf.index); } } @@ -287,9 +287,9 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) buf = list_entry(dev->vbi_out_active.next, struct vivid_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(dev, 2, "vbi_out buffer %d done\n", - buf->vb.v4l2_buf.index); + buf->vb.vb2_buf.index); } } diff --git a/kernel/drivers/media/platform/vivid/vivid-osd.c b/kernel/drivers/media/platform/vivid/vivid-osd.c index 084d346fb..e15eef6a9 100644 --- a/kernel/drivers/media/platform/vivid/vivid-osd.c +++ b/kernel/drivers/media/platform/vivid/vivid-osd.c @@ -85,6 +85,7 @@ static int vivid_fb_ioctl(struct fb_info *info, unsigned cmd, unsigned long arg) case FBIOGET_VBLANK: { struct fb_vblank vblank; + memset(&vblank, 0, sizeof(vblank)); vblank.flags = FB_VBLANK_HAVE_COUNT | FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC; vblank.count = 0; diff --git a/kernel/drivers/media/platform/vivid/vivid-radio-rx.c b/kernel/drivers/media/platform/vivid/vivid-radio-rx.c index c7651a506..f99092ca8 100644 --- a/kernel/drivers/media/platform/vivid/vivid-radio-rx.c +++ b/kernel/drivers/media/platform/vivid/vivid-radio-rx.c @@ -195,6 +195,8 @@ int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2 if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow && dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh) break; + if (band == TOT_BANDS) + return -EINVAL; low = vivid_radio_bands[band].rangelow; high = vivid_radio_bands[band].rangehigh; } diff --git a/kernel/drivers/media/platform/vivid/vivid-sdr-cap.c b/kernel/drivers/media/platform/vivid/vivid-sdr-cap.c index caf131666..082c40176 100644 --- a/kernel/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/kernel/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,25 @@ #include "vivid-ctrls.h" #include "vivid-sdr-cap.h" +/* stream formats */ +struct vivid_format { + u32 pixelformat; + u32 buffersize; +}; + +/* format descriptions for capture and preview */ +static const struct vivid_format formats[] = { + { + .pixelformat = V4L2_SDR_FMT_CU8, + .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, + }, { + .pixelformat = V4L2_SDR_FMT_CS8, + .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + static const struct v4l2_frequency_band bands_adc[] = { { .tuner = 0, @@ -95,11 +115,11 @@ static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev) spin_unlock(&dev->slock); if (sdr_cap_buf) { - sdr_cap_buf->vb.v4l2_buf.sequence = dev->sdr_cap_seq_count; + sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count; vivid_sdr_cap_process(dev, sdr_cap_buf); - v4l2_get_timestamp(&sdr_cap_buf->vb.v4l2_buf.timestamp); - sdr_cap_buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; - vb2_buffer_done(&sdr_cap_buf->vb, dev->dqbuf_error ? + v4l2_get_timestamp(&sdr_cap_buf->vb.timestamp); + sdr_cap_buf->vb.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&sdr_cap_buf->vb.vb2_buf, dev->dqbuf_error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); dev->dqbuf_error = false; } @@ -142,7 +162,8 @@ static int vivid_thread_sdr_cap(void *data) /* Calculate the number of jiffies since we started streaming */ jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap; /* Get the number of buffers streamed since the start */ - buffers_since_start = (u64)jiffies_since_start * dev->sdr_adc_freq + + buffers_since_start = + (u64)jiffies_since_start * dev->sdr_adc_freq + (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2; do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF); @@ -157,7 +178,8 @@ static int vivid_thread_sdr_cap(void *data) dev->sdr_cap_seq_offset = buffers_since_start; buffers_since_start = 0; } - dev->sdr_cap_seq_count = buffers_since_start + dev->sdr_cap_seq_offset; + dev->sdr_cap_seq_count = + buffers_since_start + dev->sdr_cap_seq_offset; vivid_thread_sdr_cap_tick(dev); mutex_unlock(&dev->mutex); @@ -191,7 +213,7 @@ static int vivid_thread_sdr_cap(void *data) return 0; } -static int sdr_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int sdr_cap_queue_setup(struct vb2_queue *vq, const void *parg, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], void *alloc_ctxs[]) { @@ -228,8 +250,9 @@ static int sdr_cap_buf_prepare(struct vb2_buffer *vb) static void sdr_cap_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -263,7 +286,8 @@ static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count) list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } return err; @@ -280,9 +304,10 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq) while (!list_empty(&dev->sdr_cap_active)) { struct vivid_buffer *buf; - buf = list_entry(dev->sdr_cap_active.next, struct vivid_buffer, list); + buf = list_entry(dev->sdr_cap_active.next, + struct vivid_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } /* shutdown control thread */ @@ -302,7 +327,8 @@ const struct vb2_ops vivid_sdr_cap_qops = { .wait_finish = vb2_ops_wait_finish, }; -int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) +int vivid_sdr_enum_freq_bands(struct file *file, void *fh, + struct v4l2_frequency_band *band) { switch (band->tuner) { case 0: @@ -320,7 +346,8 @@ int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency } } -int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +int vivid_sdr_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *vf) { struct vivid_dev *dev = video_drvdata(file); @@ -338,7 +365,8 @@ int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf } } -int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) +int vivid_sdr_s_frequency(struct file *file, void *fh, + const struct v4l2_frequency *vf) { struct vivid_dev *dev = video_drvdata(file); unsigned freq = vf->frequency; @@ -384,14 +412,16 @@ int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) case 0: strlcpy(vt->name, "ADC", sizeof(vt->name)); vt->type = V4L2_TUNER_ADC; - vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + vt->capability = + V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; vt->rangelow = bands_adc[0].rangelow; vt->rangehigh = bands_adc[2].rangehigh; return 0; case 1: strlcpy(vt->name, "RF", sizeof(vt->name)); vt->type = V4L2_TUNER_RF; - vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + vt->capability = + V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; vt->rangelow = bands_fm[0].rangelow; vt->rangehigh = bands_fm[0].rangehigh; return 0; @@ -409,65 +439,102 @@ int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) { - if (f->index) + if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; - f->pixelformat = V4L2_SDR_FMT_CU8; - strlcpy(f->description, "IQ U8", sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; return 0; } int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.sdr.pixelformat = V4L2_SDR_FMT_CU8; - f->fmt.sdr.buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; + struct vivid_dev *dev = video_drvdata(file); + + f->fmt.sdr.pixelformat = dev->sdr_pixelformat; + f->fmt.sdr.buffersize = dev->sdr_buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + return 0; +} + +int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_sdr_cap_q; + int i; + + if (vb2_is_busy(q)) + return -EBUSY; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { + dev->sdr_pixelformat = formats[i].pixelformat; + dev->sdr_buffersize = formats[i].buffersize; + f->fmt.sdr.buffersize = formats[i].buffersize; + return 0; + } + } + dev->sdr_pixelformat = formats[0].pixelformat; + dev->sdr_buffersize = formats[0].buffersize; + f->fmt.sdr.pixelformat = formats[0].pixelformat; + f->fmt.sdr.buffersize = formats[0].buffersize; + return 0; +} + +int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + int i; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + 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; return 0; } #define FIXP_N (15) #define FIXP_FRAC (1 << FIXP_N) #define FIXP_2PI ((int)(2 * 3.141592653589 * FIXP_FRAC)) +#define M_100000PI (3.14159 * 100000) void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) { - u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0); + u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); unsigned long i; - unsigned long plane_size = vb2_plane_size(&buf->vb, 0); + unsigned long plane_size = vb2_plane_size(&buf->vb.vb2_buf, 0); + s64 s64tmp; s32 src_phase_step; s32 mod_phase_step; s32 fixp_i; s32 fixp_q; - /* - * TODO: Generated beep tone goes very crackly when sample rate is - * increased to ~1Msps or more. That is because of huge rounding error - * of phase angle caused by used cosine implementation. - */ - /* calculate phase step */ #define BEEP_FREQ 1000 /* 1kHz beep */ src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ, - dev->sdr_adc_freq); + dev->sdr_adc_freq); for (i = 0; i < plane_size; i += 2) { mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase, FIXP_2PI) >> (31 - FIXP_N); dev->sdr_fixp_src_phase += src_phase_step; - dev->sdr_fixp_mod_phase += mod_phase_step / 4; + s64tmp = (s64) mod_phase_step * dev->sdr_fm_deviation; + dev->sdr_fixp_mod_phase += div_s64(s64tmp, M_100000PI); /* - * Transfer phases to [0 / 2xPI] in order to avoid variable + * Transfer phase angle to [0, 2xPI] in order to avoid variable * overflow and make it suitable for cosine implementation * used, which does not support negative angles. */ - while (dev->sdr_fixp_mod_phase < FIXP_2PI) - dev->sdr_fixp_mod_phase += FIXP_2PI; - while (dev->sdr_fixp_mod_phase > FIXP_2PI) - dev->sdr_fixp_mod_phase -= FIXP_2PI; + dev->sdr_fixp_src_phase %= FIXP_2PI; + dev->sdr_fixp_mod_phase %= FIXP_2PI; - while (dev->sdr_fixp_src_phase > FIXP_2PI) - dev->sdr_fixp_src_phase -= FIXP_2PI; + if (dev->sdr_fixp_mod_phase < 0) + dev->sdr_fixp_mod_phase += FIXP_2PI; fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); @@ -477,11 +544,25 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) fixp_i >>= (31 - FIXP_N); fixp_q >>= (31 - FIXP_N); - /* convert 'fixp float' to u8 */ - /* u8 = X * 127.5f + 127.5f; where X is float [-1.0 / +1.0] */ - fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275; - fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275; - *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); - *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + switch (dev->sdr_pixelformat) { + case V4L2_SDR_FMT_CU8: + /* convert 'fixp float' to u8 [0, +255] */ + /* u8 = X * 127.5 + 127.5; X is float [-1.0, +1.0] */ + fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275; + fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275; + *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); + *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + break; + case V4L2_SDR_FMT_CS8: + /* convert 'fixp float' to s8 [-128, +127] */ + /* s8 = X * 127.5 - 0.5; X is float [-1.0, +1.0] */ + fixp_i = fixp_i * 1275 - FIXP_FRAC * 5; + fixp_q = fixp_q * 1275 - FIXP_FRAC * 5; + *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); + *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + break; + default: + break; + } } } diff --git a/kernel/drivers/media/platform/vivid/vivid-sdr-cap.h b/kernel/drivers/media/platform/vivid/vivid-sdr-cap.h index 79c1890de..43014b273 100644 --- a/kernel/drivers/media/platform/vivid/vivid-sdr-cap.h +++ b/kernel/drivers/media/platform/vivid/vivid-sdr-cap.h @@ -27,6 +27,8 @@ int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt); int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt); int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f); int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); +int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); +int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf); extern const struct vb2_ops vivid_sdr_cap_qops; diff --git a/kernel/drivers/media/platform/vivid/vivid-tpg-colors.c b/kernel/drivers/media/platform/vivid/vivid-tpg-colors.c index 424aa7abc..2299f0ce4 100644 --- a/kernel/drivers/media/platform/vivid/vivid-tpg-colors.c +++ b/kernel/drivers/media/platform/vivid/vivid-tpg-colors.c @@ -598,71 +598,511 @@ const unsigned short tpg_linear_to_rec709[255 * 16 + 1] = { }; /* Generated table */ -const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1] = { - [V4L2_COLORSPACE_SMPTE170M][0] = { 2939, 2939, 2939 }, - [V4L2_COLORSPACE_SMPTE170M][1] = { 2953, 2963, 586 }, - [V4L2_COLORSPACE_SMPTE170M][2] = { 0, 2967, 2937 }, - [V4L2_COLORSPACE_SMPTE170M][3] = { 88, 2990, 575 }, - [V4L2_COLORSPACE_SMPTE170M][4] = { 3016, 259, 2933 }, - [V4L2_COLORSPACE_SMPTE170M][5] = { 3030, 405, 558 }, - [V4L2_COLORSPACE_SMPTE170M][6] = { 478, 428, 2931 }, - [V4L2_COLORSPACE_SMPTE170M][7] = { 547, 547, 547 }, - [V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 }, - [V4L2_COLORSPACE_SMPTE240M][1] = { 2941, 2950, 546 }, - [V4L2_COLORSPACE_SMPTE240M][2] = { 0, 2954, 2924 }, - [V4L2_COLORSPACE_SMPTE240M][3] = { 78, 2978, 536 }, - [V4L2_COLORSPACE_SMPTE240M][4] = { 3004, 230, 2920 }, - [V4L2_COLORSPACE_SMPTE240M][5] = { 3018, 363, 518 }, - [V4L2_COLORSPACE_SMPTE240M][6] = { 437, 387, 2918 }, - [V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 }, - [V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 }, - [V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 }, - [V4L2_COLORSPACE_REC709][2] = { 547, 2939, 2939 }, - [V4L2_COLORSPACE_REC709][3] = { 547, 2939, 547 }, - [V4L2_COLORSPACE_REC709][4] = { 2939, 547, 2939 }, - [V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 }, - [V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 }, - [V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 }, - [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2892, 2988, 2807 }, - [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2846, 3070, 843 }, - [V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 }, - [V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 }, - [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2476, 229, 2742 }, - [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2420, 672, 614 }, - [V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 }, - [V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 464 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 464 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2956 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2956 }, - [V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 }, - [V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 }, - [V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 }, - [V4L2_COLORSPACE_SRGB][2] = { 800, 3056, 3056 }, - [V4L2_COLORSPACE_SRGB][3] = { 800, 3056, 800 }, - [V4L2_COLORSPACE_SRGB][4] = { 3056, 800, 3056 }, - [V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 }, - [V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 }, - [V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 }, - [V4L2_COLORSPACE_ADOBERGB][0] = { 3033, 3033, 3033 }, - [V4L2_COLORSPACE_ADOBERGB][1] = { 3033, 3033, 1063 }, - [V4L2_COLORSPACE_ADOBERGB][2] = { 1828, 3033, 3033 }, - [V4L2_COLORSPACE_ADOBERGB][3] = { 1828, 3033, 1063 }, - [V4L2_COLORSPACE_ADOBERGB][4] = { 2633, 851, 2979 }, - [V4L2_COLORSPACE_ADOBERGB][5] = { 2633, 851, 851 }, - [V4L2_COLORSPACE_ADOBERGB][6] = { 851, 851, 2979 }, - [V4L2_COLORSPACE_ADOBERGB][7] = { 851, 851, 851 }, - [V4L2_COLORSPACE_BT2020][0] = { 2939, 2939, 2939 }, - [V4L2_COLORSPACE_BT2020][1] = { 2877, 2923, 1058 }, - [V4L2_COLORSPACE_BT2020][2] = { 1837, 2840, 2916 }, - [V4L2_COLORSPACE_BT2020][3] = { 1734, 2823, 993 }, - [V4L2_COLORSPACE_BT2020][4] = { 2427, 961, 2812 }, - [V4L2_COLORSPACE_BT2020][5] = { 2351, 912, 648 }, - [V4L2_COLORSPACE_BT2020][6] = { 792, 618, 2788 }, - [V4L2_COLORSPACE_BT2020][7] = { 547, 547, 547 }, +const struct color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1][V4L2_XFER_FUNC_SMPTE2084 + 1][TPG_COLOR_CSC_BLACK + 1] = { + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][1] = { 2953, 2963, 586 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][2] = { 0, 2967, 2937 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][3] = { 88, 2990, 575 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][4] = { 3016, 259, 2933 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][5] = { 3030, 405, 558 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][6] = { 478, 428, 2931 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][1] = { 3068, 3077, 838 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][2] = { 0, 3081, 3053 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][3] = { 241, 3102, 828 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][4] = { 3126, 504, 3050 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][5] = { 3138, 657, 810 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][6] = { 731, 680, 3048 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][1] = { 3046, 3054, 886 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][2] = { 0, 3058, 3031 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][3] = { 360, 3079, 877 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][4] = { 3103, 587, 3027 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][5] = { 3116, 723, 861 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][6] = { 789, 744, 3025 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2941, 2950, 546 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][2] = { 0, 2954, 2924 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][3] = { 78, 2978, 536 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][4] = { 3004, 230, 2920 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][5] = { 3018, 363, 518 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][6] = { 437, 387, 2918 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][1] = { 2145, 2159, 142 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][2] = { 0, 2164, 2122 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][3] = { 19, 2198, 138 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][4] = { 2236, 57, 2116 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][5] = { 2256, 90, 133 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][6] = { 110, 96, 2113 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][1] = { 3186, 3194, 1121 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][2] = { 0, 3197, 3173 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][3] = { 523, 3216, 1112 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][4] = { 3237, 792, 3169 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][5] = { 3248, 944, 1094 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][6] = { 1017, 967, 3168 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][1] = { 3802, 3805, 2602 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][2] = { 0, 3806, 3797 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][3] = { 1780, 3812, 2592 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][4] = { 3820, 2215, 3796 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][5] = { 3824, 2409, 2574 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][6] = { 2491, 2435, 3795 }, + [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][1] = { 2953, 2963, 586 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][2] = { 0, 2967, 2937 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][3] = { 88, 2990, 575 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][4] = { 3016, 259, 2933 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][5] = { 3030, 405, 558 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][6] = { 478, 428, 2931 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][1] = { 3068, 3077, 838 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][2] = { 0, 3081, 3053 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][3] = { 241, 3102, 828 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][4] = { 3126, 504, 3050 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][5] = { 3138, 657, 810 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][6] = { 731, 680, 3048 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][1] = { 3046, 3054, 886 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][2] = { 0, 3058, 3031 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][3] = { 360, 3079, 877 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][4] = { 3103, 587, 3027 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][5] = { 3116, 723, 861 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][6] = { 789, 744, 3025 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2941, 2950, 546 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][2] = { 0, 2954, 2924 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][3] = { 78, 2978, 536 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][4] = { 3004, 230, 2920 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][5] = { 3018, 363, 518 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][6] = { 437, 387, 2918 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][1] = { 2145, 2159, 142 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][2] = { 0, 2164, 2122 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][3] = { 19, 2198, 138 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][4] = { 2236, 57, 2116 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][5] = { 2256, 90, 133 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][6] = { 110, 96, 2113 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][1] = { 3186, 3194, 1121 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][2] = { 0, 3197, 3173 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][3] = { 523, 3216, 1112 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][4] = { 3237, 792, 3169 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][5] = { 3248, 944, 1094 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][6] = { 1017, 967, 3168 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][1] = { 3802, 3805, 2602 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][2] = { 0, 3806, 3797 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][3] = { 1780, 3812, 2592 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][4] = { 3820, 2215, 3796 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][5] = { 3824, 2409, 2574 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][6] = { 2491, 2435, 3795 }, + [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 547 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][2] = { 547, 2939, 2939 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][3] = { 547, 2939, 547 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][4] = { 2939, 547, 2939 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][5] = { 2939, 547, 547 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][6] = { 547, 547, 2939 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 800 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][2] = { 800, 3056, 3056 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][3] = { 800, 3056, 800 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][4] = { 3056, 800, 3056 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][5] = { 3056, 800, 800 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3056 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][1] = { 3033, 3033, 851 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][2] = { 851, 3033, 3033 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][3] = { 851, 3033, 851 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][4] = { 3033, 851, 3033 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][5] = { 3033, 851, 851 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][6] = { 851, 851, 3033 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 507 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][2] = { 507, 2926, 2926 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][3] = { 507, 2926, 507 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][4] = { 2926, 507, 2926 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][5] = { 2926, 507, 507 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2926 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 130 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][2] = { 130, 2125, 2125 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][3] = { 130, 2125, 130 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][4] = { 2125, 130, 2125 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][5] = { 2125, 130, 130 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2125 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1084 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][2] = { 1084, 3175, 3175 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][3] = { 1084, 3175, 1084 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][4] = { 3175, 1084, 3175 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][5] = { 3175, 1084, 1084 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3175 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][1] = { 3798, 3798, 2563 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][2] = { 2563, 3798, 3798 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][3] = { 2563, 3798, 2563 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][4] = { 3798, 2563, 3798 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][5] = { 3798, 2563, 2563 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][6] = { 2563, 2563, 3798 }, + [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][1] = { 2892, 3034, 910 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][2] = { 1715, 2916, 2914 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][3] = { 1631, 3012, 828 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][4] = { 2497, 119, 2867 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][5] = { 2440, 649, 657 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][6] = { 740, 0, 2841 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3055, 3056 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][1] = { 3013, 3142, 1157 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][2] = { 1926, 3034, 3032 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][3] = { 1847, 3121, 1076 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][4] = { 2651, 304, 2990 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][5] = { 2599, 901, 909 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][6] = { 991, 0, 2966 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][1] = { 2989, 3120, 1180 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][2] = { 1913, 3011, 3009 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][3] = { 1836, 3099, 1105 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][4] = { 2627, 413, 2966 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][5] = { 2576, 943, 951 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][6] = { 1026, 0, 2942 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2879, 3022, 874 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][2] = { 1688, 2903, 2901 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][3] = { 1603, 2999, 791 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][4] = { 2479, 106, 2853 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][5] = { 2422, 610, 618 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][6] = { 702, 0, 2827 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][1] = { 2059, 2262, 266 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][2] = { 771, 2092, 2089 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][3] = { 705, 2229, 231 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][4] = { 1550, 26, 2024 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][5] = { 1484, 163, 165 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][6] = { 196, 0, 1988 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][1] = { 3136, 3251, 1429 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][2] = { 2150, 3156, 3154 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][3] = { 2077, 3233, 1352 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][4] = { 2812, 589, 3116 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][5] = { 2765, 1182, 1190 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][6] = { 1270, 0, 3094 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][1] = { 3784, 3825, 2879 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][2] = { 3351, 3791, 3790 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][3] = { 3311, 3819, 2815 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][4] = { 3659, 1900, 3777 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][5] = { 3640, 2662, 2669 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][6] = { 2743, 0, 3769 }, + [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 464 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][2] = { 786, 2939, 2939 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][3] = { 786, 2939, 464 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][4] = { 2879, 547, 2956 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][5] = { 2879, 547, 547 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][6] = { 547, 547, 2956 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 717 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][2] = { 1036, 3056, 3056 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][3] = { 1036, 3056, 717 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][4] = { 3001, 800, 3071 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][5] = { 3001, 800, 799 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3071 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 799 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][1] = { 3033, 3033, 776 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][2] = { 1068, 3033, 3033 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][3] = { 1068, 3033, 776 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][4] = { 2977, 851, 3048 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][5] = { 2977, 851, 851 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][6] = { 851, 851, 3048 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 423 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][2] = { 749, 2926, 2926 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][3] = { 749, 2926, 423 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][4] = { 2865, 507, 2943 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][5] = { 2865, 507, 507 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2943 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 106 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][2] = { 214, 2125, 2125 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][3] = { 214, 2125, 106 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][4] = { 2041, 130, 2149 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][5] = { 2041, 130, 130 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2149 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1003 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][2] = { 1313, 3175, 3175 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][3] = { 1313, 3175, 1003 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][4] = { 3126, 1084, 3188 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][5] = { 3126, 1084, 1084 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3188 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][1] = { 3798, 3798, 2476 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][2] = { 2782, 3798, 3798 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][3] = { 2782, 3798, 2476 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][4] = { 3780, 2563, 3803 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][5] = { 3780, 2563, 2563 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][6] = { 2563, 2563, 3803 }, + [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 547 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][2] = { 547, 2939, 2939 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][3] = { 547, 2939, 547 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][4] = { 2939, 547, 2939 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][5] = { 2939, 547, 547 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][6] = { 547, 547, 2939 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 800 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][2] = { 800, 3056, 3056 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][3] = { 800, 3056, 800 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][4] = { 3056, 800, 3056 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][5] = { 3056, 800, 800 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3056 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][1] = { 3033, 3033, 851 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][2] = { 851, 3033, 3033 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][3] = { 851, 3033, 851 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][4] = { 3033, 851, 3033 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][5] = { 3033, 851, 851 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][6] = { 851, 851, 3033 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 507 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][2] = { 507, 2926, 2926 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][3] = { 507, 2926, 507 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][4] = { 2926, 507, 2926 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][5] = { 2926, 507, 507 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2926 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 130 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][2] = { 130, 2125, 2125 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][3] = { 130, 2125, 130 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][4] = { 2125, 130, 2125 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][5] = { 2125, 130, 130 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2125 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1084 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][2] = { 1084, 3175, 3175 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][3] = { 1084, 3175, 1084 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][4] = { 3175, 1084, 3175 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][5] = { 3175, 1084, 1084 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3175 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][1] = { 3798, 3798, 2563 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][2] = { 2563, 3798, 3798 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][3] = { 2563, 3798, 2563 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][4] = { 3798, 2563, 3798 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][5] = { 3798, 2563, 2563 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][6] = { 2563, 2563, 3798 }, + [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 781 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][2] = { 1622, 2939, 2939 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][3] = { 1622, 2939, 781 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][4] = { 2502, 547, 2881 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][5] = { 2502, 547, 547 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][6] = { 547, 547, 2881 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 1031 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][2] = { 1838, 3056, 3056 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][3] = { 1838, 3056, 1031 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][4] = { 2657, 800, 3002 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][5] = { 2657, 800, 800 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3002 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][1] = { 3033, 3033, 1063 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][2] = { 1828, 3033, 3033 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][3] = { 1828, 3033, 1063 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][4] = { 2633, 851, 2979 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][5] = { 2633, 851, 851 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][6] = { 851, 851, 2979 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 744 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][2] = { 1594, 2926, 2926 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][3] = { 1594, 2926, 744 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][4] = { 2484, 507, 2867 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][5] = { 2484, 507, 507 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2867 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 212 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][2] = { 698, 2125, 2125 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][3] = { 698, 2125, 212 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][4] = { 1557, 130, 2043 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][5] = { 1557, 130, 130 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2043 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1308 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][2] = { 2069, 3175, 3175 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][3] = { 2069, 3175, 1308 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][4] = { 2816, 1084, 3127 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][5] = { 2816, 1084, 1084 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3127 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][1] = { 3798, 3798, 2778 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][2] = { 3306, 3798, 3798 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][3] = { 3306, 3798, 2778 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][4] = { 3661, 2563, 3781 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][5] = { 3661, 2563, 2563 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][6] = { 2563, 2563, 3781 }, + [V4L2_COLORSPACE_ADOBERGB][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][1] = { 2877, 2923, 1058 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][2] = { 1837, 2840, 2916 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][3] = { 1734, 2823, 993 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][4] = { 2427, 961, 2812 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][5] = { 2351, 912, 648 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][6] = { 792, 618, 2788 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][1] = { 2999, 3041, 1301 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][2] = { 2040, 2965, 3034 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][3] = { 1944, 2950, 1238 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][4] = { 2587, 1207, 2940 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][5] = { 2517, 1159, 900 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][6] = { 1042, 870, 2917 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][1] = { 2976, 3018, 1315 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][2] = { 2024, 2942, 3011 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][3] = { 1930, 2926, 1256 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][4] = { 2563, 1227, 2916 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][5] = { 2494, 1183, 943 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][6] = { 1073, 916, 2894 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][1] = { 2864, 2910, 1024 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][2] = { 1811, 2826, 2903 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][3] = { 1707, 2809, 958 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][4] = { 2408, 926, 2798 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][5] = { 2331, 876, 609 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][6] = { 755, 579, 2773 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][1] = { 2039, 2102, 338 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][2] = { 873, 1987, 2092 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][3] = { 787, 1965, 305 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][4] = { 1468, 290, 1949 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][5] = { 1382, 268, 162 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][6] = { 216, 152, 1917 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][1] = { 3124, 3161, 1566 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][2] = { 2255, 3094, 3156 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][3] = { 2166, 3080, 1506 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][4] = { 2754, 1477, 3071 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][5] = { 2690, 1431, 1182 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][6] = { 1318, 1153, 3051 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][1] = { 3780, 3793, 2984 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][2] = { 3406, 3768, 3791 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][3] = { 3359, 3763, 2939 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][4] = { 3636, 2916, 3760 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][5] = { 3609, 2880, 2661 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][6] = { 2786, 2633, 3753 }, + [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][1] = { 2936, 2934, 992 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][2] = { 1159, 2890, 2916 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][3] = { 1150, 2885, 921 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][4] = { 2751, 766, 2837 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][5] = { 2747, 747, 650 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][6] = { 563, 570, 2812 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3055 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][1] = { 3052, 3051, 1237 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][2] = { 1397, 3011, 3034 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][3] = { 1389, 3006, 1168 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][4] = { 2884, 1016, 2962 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][5] = { 2880, 998, 902 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][6] = { 816, 823, 2940 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 799 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][0] = { 3033, 3033, 3033 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][1] = { 3029, 3028, 1255 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][2] = { 1406, 2988, 3011 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][3] = { 1398, 2983, 1190 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][4] = { 2860, 1050, 2939 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][5] = { 2857, 1033, 945 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][6] = { 866, 873, 2916 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_ADOBERGB][7] = { 851, 851, 851 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][1] = { 2923, 2921, 957 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][2] = { 1125, 2877, 2902 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][3] = { 1116, 2871, 885 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][4] = { 2736, 729, 2823 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][5] = { 2732, 710, 611 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][6] = { 523, 531, 2798 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][1] = { 2120, 2118, 305 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][2] = { 392, 2056, 2092 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][3] = { 387, 2049, 271 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][4] = { 1868, 206, 1983 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][5] = { 1863, 199, 163 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][6] = { 135, 137, 1950 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][1] = { 3172, 3170, 1505 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][2] = { 1657, 3135, 3155 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][3] = { 1649, 3130, 1439 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][4] = { 3021, 1294, 3091 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][5] = { 3018, 1276, 1184 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][6] = { 1100, 1107, 3071 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][0] = { 3798, 3798, 3798 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][1] = { 3797, 3796, 2938 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][2] = { 3049, 3783, 3791 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][3] = { 3044, 3782, 2887 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][4] = { 3741, 2765, 3768 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][5] = { 3740, 2749, 2663 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][6] = { 2580, 2587, 3760 }, + [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][7] = { 2563, 2563, 2563 }, }; #else @@ -674,9 +1114,13 @@ const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BL #include static const double rec709_to_ntsc1953[3][3] = { - { 0.6689794, 0.2678309, 0.0323187 }, - { 0.0184901, 1.0742442, -0.0602820 }, - { 0.0162259, 0.0431716, 0.8549253 } + /* + * This transform uses the Bradford method to compensate for + * the different whitepoints. + */ + { 0.6785011, 0.2883441, 0.0331548 }, + { 0.0165284, 1.0518725, -0.0684009 }, + { 0.0179230, 0.0506096, 0.9314674 } }; static const double rec709_to_ebu[3][3] = { @@ -709,6 +1153,16 @@ static const double rec709_to_bt2020[3][3] = { { 0.0163976, 0.0880301, 0.8955723 }, }; +static const double rec709_to_dcip3[3][3] = { + /* + * This transform uses the Bradford method to compensate for + * the different whitepoints. + */ + { 0.8686648, 0.1288456, 0.0024896 }, + { 0.0345479, 0.9618084, 0.0036437 }, + { 0.0167785, 0.0710559, 0.9121655 } +}; + static void mult_matrix(double *r, double *g, double *b, const double m[3][3]) { double ir, ig, ib; @@ -759,55 +1213,63 @@ static double transfer_rgb_to_adobergb(double v) return pow(v, 1.0 / 2.19921875); } +static double transfer_rgb_to_dcip3(double v) +{ + return pow(v, 1.0 / 2.6); +} + +static double transfer_rgb_to_smpte2084(double v) +{ + const double m1 = (2610.0 / 4096.0) / 4.0; + const double m2 = 128.0 * 2523.0 / 4096.0; + const double c1 = 3424.0 / 4096.0; + const double c2 = 32.0 * 2413.0 / 4096.0; + const double c3 = 32.0 * 2392.0 / 4096.0; + + v = pow(v, m1); + return pow((c1 + c2 * v) / (1 + c3 * v), m2); +} + static double transfer_srgb_to_rec709(double v) { return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v)); } -static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b) +static void csc(enum v4l2_colorspace colorspace, enum v4l2_xfer_func xfer_func, + double *r, double *g, double *b) { int clamp = 1; + *r = transfer_srgb_to_rgb(*r); + *g = transfer_srgb_to_rgb(*g); + *b = transfer_srgb_to_rgb(*b); + /* Convert the primaries of Rec. 709 Linear RGB */ switch (colorspace) { case V4L2_COLORSPACE_SMPTE240M: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_240m); break; case V4L2_COLORSPACE_SMPTE170M: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_170m); break; case V4L2_COLORSPACE_470_SYSTEM_BG: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_ebu); break; case V4L2_COLORSPACE_470_SYSTEM_M: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_ntsc1953); break; case V4L2_COLORSPACE_ADOBERGB: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_adobergb); break; case V4L2_COLORSPACE_BT2020: - *r = transfer_srgb_to_rgb(*r); - *g = transfer_srgb_to_rgb(*g); - *b = transfer_srgb_to_rgb(*b); mult_matrix(r, g, b, rec709_to_bt2020); break; + case V4L2_COLORSPACE_DCI_P3: + mult_matrix(r, g, b, rec709_to_dcip3); + break; case V4L2_COLORSPACE_SRGB: case V4L2_COLORSPACE_REC709: + break; default: break; } @@ -818,33 +1280,38 @@ static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b)); } - /* Encode to gamma corrected colorspace */ - switch (colorspace) { - case V4L2_COLORSPACE_SMPTE240M: - *r = transfer_rgb_to_smpte240m(*r); - *g = transfer_rgb_to_smpte240m(*g); - *b = transfer_rgb_to_smpte240m(*b); - break; - case V4L2_COLORSPACE_SMPTE170M: - case V4L2_COLORSPACE_470_SYSTEM_M: - case V4L2_COLORSPACE_470_SYSTEM_BG: - case V4L2_COLORSPACE_BT2020: + switch (xfer_func) { + case V4L2_XFER_FUNC_709: *r = transfer_rgb_to_rec709(*r); *g = transfer_rgb_to_rec709(*g); *b = transfer_rgb_to_rec709(*b); break; - case V4L2_COLORSPACE_SRGB: + case V4L2_XFER_FUNC_SRGB: + *r = transfer_rgb_to_srgb(*r); + *g = transfer_rgb_to_srgb(*g); + *b = transfer_rgb_to_srgb(*b); break; - case V4L2_COLORSPACE_ADOBERGB: + case V4L2_XFER_FUNC_ADOBERGB: *r = transfer_rgb_to_adobergb(*r); *g = transfer_rgb_to_adobergb(*g); *b = transfer_rgb_to_adobergb(*b); break; - case V4L2_COLORSPACE_REC709: - default: - *r = transfer_srgb_to_rec709(*r); - *g = transfer_srgb_to_rec709(*g); - *b = transfer_srgb_to_rec709(*b); + case V4L2_XFER_FUNC_DCI_P3: + *r = transfer_rgb_to_dcip3(*r); + *g = transfer_rgb_to_dcip3(*g); + *b = transfer_rgb_to_dcip3(*b); + break; + case V4L2_XFER_FUNC_SMPTE2084: + *r = transfer_rgb_to_smpte2084(*r); + *g = transfer_rgb_to_smpte2084(*g); + *b = transfer_rgb_to_smpte2084(*b); + break; + case V4L2_XFER_FUNC_SMPTE240M: + *r = transfer_rgb_to_smpte240m(*r); + *g = transfer_rgb_to_smpte240m(*g); + *b = transfer_rgb_to_smpte240m(*b); + break; + case V4L2_XFER_FUNC_NONE: break; } } @@ -863,6 +1330,8 @@ int main(int argc, char **argv) V4L2_COLORSPACE_SRGB, V4L2_COLORSPACE_ADOBERGB, V4L2_COLORSPACE_BT2020, + 0, + V4L2_COLORSPACE_DCI_P3, }; static const char * const colorspace_names[] = { "", @@ -876,8 +1345,21 @@ int main(int argc, char **argv) "V4L2_COLORSPACE_SRGB", "V4L2_COLORSPACE_ADOBERGB", "V4L2_COLORSPACE_BT2020", + "", + "V4L2_COLORSPACE_DCI_P3", + }; + static const char * const xfer_func_names[] = { + "", + "V4L2_XFER_FUNC_709", + "V4L2_XFER_FUNC_SRGB", + "V4L2_XFER_FUNC_ADOBERGB", + "V4L2_XFER_FUNC_SMPTE240M", + "V4L2_XFER_FUNC_NONE", + "V4L2_XFER_FUNC_DCI_P3", + "V4L2_XFER_FUNC_SMPTE2084", }; int i; + int x; int c; printf("/* Generated table */\n"); @@ -905,22 +1387,26 @@ int main(int argc, char **argv) printf("\n};\n\n"); printf("/* Generated table */\n"); - printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1] = {\n"); - for (c = 0; c <= V4L2_COLORSPACE_BT2020; c++) { - for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) { - double r, g, b; + printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1][V4L2_XFER_FUNC_SMPTE2084 + 1][TPG_COLOR_CSC_BLACK + 1] = {\n"); + for (c = 0; c <= V4L2_COLORSPACE_DCI_P3; c++) { + for (x = 1; x <= V4L2_XFER_FUNC_SMPTE2084; x++) { + for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) { + double r, g, b; - if (colorspaces[c] == 0) - continue; + if (colorspaces[c] == 0) + continue; - r = tpg_colors[i].r / 255.0; - g = tpg_colors[i].g / 255.0; - b = tpg_colors[i].b / 255.0; + r = tpg_colors[i].r / 255.0; + g = tpg_colors[i].g / 255.0; + b = tpg_colors[i].b / 255.0; - csc(c, &r, &g, &b); + csc(c, x, &r, &g, &b); - printf("\t[%s][%d] = { %d, %d, %d },\n", colorspace_names[c], i, - (int)(r * 4080), (int)(g * 4080), (int)(b * 4080)); + printf("\t[%s][%s][%d] = { %d, %d, %d },\n", + colorspace_names[c], + xfer_func_names[x], i, + (int)(r * 4080), (int)(g * 4080), (int)(b * 4080)); + } } } printf("};\n\n"); diff --git a/kernel/drivers/media/platform/vivid/vivid-tpg-colors.h b/kernel/drivers/media/platform/vivid/vivid-tpg-colors.h index 2c3333564..4e5a76a1e 100644 --- a/kernel/drivers/media/platform/vivid/vivid-tpg-colors.h +++ b/kernel/drivers/media/platform/vivid/vivid-tpg-colors.h @@ -61,6 +61,8 @@ enum tpg_color { extern const struct color tpg_colors[TPG_COLOR_MAX]; extern const unsigned short tpg_rec709_to_linear[255 * 16 + 1]; extern const unsigned short tpg_linear_to_rec709[255 * 16 + 1]; -extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1]; +extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1] + [V4L2_XFER_FUNC_SMPTE2084 + 1] + [TPG_COLOR_CSC_BLACK + 1]; #endif diff --git a/kernel/drivers/media/platform/vivid/vivid-tpg.c b/kernel/drivers/media/platform/vivid/vivid-tpg.c index cb766eb15..14256141f 100644 --- a/kernel/drivers/media/platform/vivid/vivid-tpg.c +++ b/kernel/drivers/media/platform/vivid/vivid-tpg.c @@ -193,6 +193,14 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: tpg->interleaved = true; tpg->vdownsampling[1] = 1; tpg->hdownsampling[1] = 1; @@ -220,6 +228,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_ABGR32: case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y16_BE: tpg->is_yuv = false; break; case V4L2_PIX_FMT_YUV444: @@ -292,6 +302,7 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) } switch (fourcc) { + case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_RGB332: tpg->twopixelsize[0] = 2; break; @@ -313,6 +324,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_YUV444: case V4L2_PIX_FMT_YUV555: case V4L2_PIX_FMT_YUV565: + case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y16_BE: tpg->twopixelsize[0] = 2 * 2; break; case V4L2_PIX_FMT_RGB24: @@ -329,9 +342,6 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_YUV32: tpg->twopixelsize[0] = 2 * 4; break; - case V4L2_PIX_FMT_GREY: - tpg->twopixelsize[0] = 2; - break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV12M: @@ -347,6 +357,17 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) tpg->twopixelsize[0] = 2; tpg->twopixelsize[1] = 2; break; + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SBGGR12: + tpg->twopixelsize[0] = 4; + tpg->twopixelsize[1] = 4; + break; case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -479,44 +500,71 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) }, { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) }, }; + static const int smpte240m_full[3][3] = { + { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) }, + { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) }, + { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) }, + }; static const int bt2020[3][3] = { { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) }, { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) }, { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) }, }; + static const int bt2020_full[3][3] = { + { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) }, + { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) }, + { COEFF(0.5, 255), COEFF(-0.4698, 255), COEFF(-0.0402, 255) }, + }; + static const int bt2020c[4] = { + COEFF(1.0 / 1.9404, 224), COEFF(1.0 / 1.5816, 224), + COEFF(1.0 / 1.7184, 224), COEFF(1.0 / 0.9936, 224), + }; + static const int bt2020c_full[4] = { + COEFF(1.0 / 1.9404, 255), COEFF(1.0 / 1.5816, 255), + COEFF(1.0 / 1.7184, 255), COEFF(1.0 / 0.9936, 255), + }; + bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; unsigned y_offset = full ? 0 : 16; int lin_y, yc; switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: - case V4L2_YCBCR_ENC_XV601: case V4L2_YCBCR_ENC_SYCC: rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr); break; + case V4L2_YCBCR_ENC_XV601: + /* Ignore quantization range, there is only one possible + * Y'CbCr encoding. */ + rgb2ycbcr(bt601, r, g, b, 16, y, cb, cr); + break; + case V4L2_YCBCR_ENC_XV709: + /* Ignore quantization range, there is only one possible + * Y'CbCr encoding. */ + rgb2ycbcr(rec709, r, g, b, 16, y, cb, cr); + break; case V4L2_YCBCR_ENC_BT2020: - rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr); + rgb2ycbcr(full ? bt2020_full : bt2020, r, g, b, y_offset, y, cb, cr); break; case V4L2_YCBCR_ENC_BT2020_CONST_LUM: lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) + COEFF(0.6780, 255) * rec709_to_linear(g) + COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16; yc = linear_to_rec709(lin_y); - *y = (yc * 219) / 255 + (16 << 4); + *y = full ? yc : (yc * 219) / 255 + (16 << 4); if (b <= yc) - *cb = (((b - yc) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4); + *cb = (((b - yc) * (full ? bt2020c_full[0] : bt2020c[0])) >> 16) + (128 << 4); else - *cb = (((b - yc) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4); + *cb = (((b - yc) * (full ? bt2020c_full[1] : bt2020c[1])) >> 16) + (128 << 4); if (r <= yc) - *cr = (((r - yc) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4); + *cr = (((r - yc) * (full ? bt2020c_full[2] : bt2020c[2])) >> 16) + (128 << 4); else - *cr = (((r - yc) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4); + *cr = (((r - yc) * (full ? bt2020c_full[3] : bt2020c[3])) >> 16) + (128 << 4); break; case V4L2_YCBCR_ENC_SMPTE240M: - rgb2ycbcr(smpte240m, r, g, b, 16, y, cb, cr); + rgb2ycbcr(full ? smpte240m_full : smpte240m, r, g, b, y_offset, y, cb, cr); break; case V4L2_YCBCR_ENC_709: - case V4L2_YCBCR_ENC_XV709: default: rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr); break; @@ -567,42 +615,71 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) }, { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) }, }; + static const int smpte240m_full[3][3] = { + { COEFF(1, 255), COEFF(0, 255), COEFF(1.5756, 255) }, + { COEFF(1, 255), COEFF(-0.2253, 255), COEFF(-0.4767, 255) }, + { COEFF(1, 255), COEFF(1.8270, 255), COEFF(0, 255) }, + }; static const int bt2020[3][3] = { { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) }, { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) }, { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) }, }; + static const int bt2020_full[3][3] = { + { COEFF(1, 255), COEFF(0, 255), COEFF(1.4746, 255) }, + { COEFF(1, 255), COEFF(-0.1646, 255), COEFF(-0.5714, 255) }, + { COEFF(1, 255), COEFF(1.8814, 255), COEFF(0, 255) }, + }; + static const int bt2020c[4] = { + COEFF(1.9404, 224), COEFF(1.5816, 224), + COEFF(1.7184, 224), COEFF(0.9936, 224), + }; + static const int bt2020c_full[4] = { + COEFF(1.9404, 255), COEFF(1.5816, 255), + COEFF(1.7184, 255), COEFF(0.9936, 255), + }; + bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; unsigned y_offset = full ? 0 : 16; + int y_fac = full ? COEFF(1.0, 255) : COEFF(1.0, 219); int lin_r, lin_g, lin_b, lin_y; switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: - case V4L2_YCBCR_ENC_XV601: case V4L2_YCBCR_ENC_SYCC: ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b); break; + case V4L2_YCBCR_ENC_XV601: + /* Ignore quantization range, there is only one possible + * Y'CbCr encoding. */ + ycbcr2rgb(bt601, y, cb, cr, 16, r, g, b); + break; + case V4L2_YCBCR_ENC_XV709: + /* Ignore quantization range, there is only one possible + * Y'CbCr encoding. */ + ycbcr2rgb(rec709, y, cb, cr, 16, r, g, b); + break; case V4L2_YCBCR_ENC_BT2020: - ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b); + ycbcr2rgb(full ? bt2020_full : bt2020, y, cb, cr, y_offset, r, g, b); break; case V4L2_YCBCR_ENC_BT2020_CONST_LUM: - y -= 16 << 4; + y -= full ? 0 : 16 << 4; cb -= 128 << 4; cr -= 128 << 4; if (cb <= 0) - *b = COEFF(1.0, 219) * y + COEFF(1.9404, 224) * cb; + *b = y_fac * y + (full ? bt2020c_full[0] : bt2020c[0]) * cb; else - *b = COEFF(1.0, 219) * y + COEFF(1.5816, 224) * cb; + *b = y_fac * y + (full ? bt2020c_full[1] : bt2020c[1]) * cb; *b = *b >> 12; if (cr <= 0) - *r = COEFF(1.0, 219) * y + COEFF(1.7184, 224) * cr; + *r = y_fac * y + (full ? bt2020c_full[2] : bt2020c[2]) * cr; else - *r = COEFF(1.0, 219) * y + COEFF(0.9936, 224) * cr; + *r = y_fac * y + (full ? bt2020c_full[3] : bt2020c[3]) * cr; *r = *r >> 12; lin_r = rec709_to_linear(*r); lin_b = rec709_to_linear(*b); - lin_y = rec709_to_linear((y * 255) / 219); + lin_y = rec709_to_linear((y * 255) / (full ? 255 : 219)); lin_g = COEFF(1.0 / 0.6780, 255) * lin_y - COEFF(0.2627 / 0.6780, 255) * lin_r - @@ -610,10 +687,9 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, *g = linear_to_rec709(lin_g >> 12); break; case V4L2_YCBCR_ENC_SMPTE240M: - ycbcr2rgb(smpte240m, y, cb, cr, 16, r, g, b); + ycbcr2rgb(full ? smpte240m_full : smpte240m, y, cb, cr, y_offset, r, g, b); break; case V4L2_YCBCR_ENC_709: - case V4L2_YCBCR_ENC_XV709: default: ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b); break; @@ -649,15 +725,17 @@ static void precalculate_color(struct tpg_data *tpg, int k) } if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) { - r = tpg_csc_colors[tpg->colorspace][col].r; - g = tpg_csc_colors[tpg->colorspace][col].g; - b = tpg_csc_colors[tpg->colorspace][col].b; + r = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].r; + g = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].g; + b = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].b; } else { r <<= 4; g <<= 4; b <<= 4; } - if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) { + if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY || + tpg->fourcc == V4L2_PIX_FMT_Y16 || + tpg->fourcc == V4L2_PIX_FMT_Y16_BE) { /* Rec. 709 Luma function */ /* (0.2126, 0.7152, 0.0722) * (255 * 256) */ r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16; @@ -840,6 +918,21 @@ static void gen_twopix(struct tpg_data *tpg, case V4L2_PIX_FMT_GREY: buf[0][offset] = r_y; break; + case V4L2_PIX_FMT_Y16: + /* + * Ideally both bytes should be set to r_y, but then you won't + * be able to detect endian problems. So keep it 0 except for + * the corner case where r_y is 0xff so white really will be + * white (0xffff). + */ + buf[0][offset] = r_y == 0xff ? r_y : 0; + buf[0][offset+1] = r_y; + break; + case V4L2_PIX_FMT_Y16_BE: + /* See comment for V4L2_PIX_FMT_Y16 above */ + buf[0][offset] = r_y; + buf[0][offset+1] = r_y == 0xff ? r_y : 0; + break; case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420M: @@ -1038,6 +1131,70 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset] = odd ? g_u : r_y; buf[1][offset] = odd ? b_v : g_u; break; + case V4L2_PIX_FMT_SBGGR10: + buf[0][offset] = odd ? g_u << 2 : b_v << 2; + buf[0][offset + 1] = odd ? g_u >> 6 : b_v >> 6; + buf[1][offset] = odd ? r_y << 2 : g_u << 2; + buf[1][offset + 1] = odd ? r_y >> 6 : g_u >> 6; + buf[0][offset] |= (buf[0][offset] >> 2) & 3; + buf[1][offset] |= (buf[1][offset] >> 2) & 3; + break; + case V4L2_PIX_FMT_SGBRG10: + buf[0][offset] = odd ? b_v << 2 : g_u << 2; + buf[0][offset + 1] = odd ? b_v >> 6 : g_u >> 6; + buf[1][offset] = odd ? g_u << 2 : r_y << 2; + buf[1][offset + 1] = odd ? g_u >> 6 : r_y >> 6; + buf[0][offset] |= (buf[0][offset] >> 2) & 3; + buf[1][offset] |= (buf[1][offset] >> 2) & 3; + break; + case V4L2_PIX_FMT_SGRBG10: + buf[0][offset] = odd ? r_y << 2 : g_u << 2; + buf[0][offset + 1] = odd ? r_y >> 6 : g_u >> 6; + buf[1][offset] = odd ? g_u << 2 : b_v << 2; + buf[1][offset + 1] = odd ? g_u >> 6 : b_v >> 6; + buf[0][offset] |= (buf[0][offset] >> 2) & 3; + buf[1][offset] |= (buf[1][offset] >> 2) & 3; + break; + case V4L2_PIX_FMT_SRGGB10: + buf[0][offset] = odd ? g_u << 2 : r_y << 2; + buf[0][offset + 1] = odd ? g_u >> 6 : r_y >> 6; + buf[1][offset] = odd ? b_v << 2 : g_u << 2; + buf[1][offset + 1] = odd ? b_v >> 6 : g_u >> 6; + buf[0][offset] |= (buf[0][offset] >> 2) & 3; + buf[1][offset] |= (buf[1][offset] >> 2) & 3; + break; + case V4L2_PIX_FMT_SBGGR12: + buf[0][offset] = odd ? g_u << 4 : b_v << 4; + buf[0][offset + 1] = odd ? g_u >> 4 : b_v >> 4; + buf[1][offset] = odd ? r_y << 4 : g_u << 4; + buf[1][offset + 1] = odd ? r_y >> 4 : g_u >> 4; + buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; + buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; + break; + case V4L2_PIX_FMT_SGBRG12: + buf[0][offset] = odd ? b_v << 4 : g_u << 4; + buf[0][offset + 1] = odd ? b_v >> 4 : g_u >> 4; + buf[1][offset] = odd ? g_u << 4 : r_y << 4; + buf[1][offset + 1] = odd ? g_u >> 4 : r_y >> 4; + buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; + buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; + break; + case V4L2_PIX_FMT_SGRBG12: + buf[0][offset] = odd ? r_y << 4 : g_u << 4; + buf[0][offset + 1] = odd ? r_y >> 4 : g_u >> 4; + buf[1][offset] = odd ? g_u << 4 : b_v << 4; + buf[1][offset + 1] = odd ? g_u >> 4 : b_v >> 4; + buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; + buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; + break; + case V4L2_PIX_FMT_SRGGB12: + buf[0][offset] = odd ? g_u << 4 : r_y << 4; + buf[0][offset + 1] = odd ? g_u >> 4 : r_y >> 4; + buf[1][offset] = odd ? b_v << 4 : g_u << 4; + buf[1][offset + 1] = odd ? b_v >> 4 : g_u >> 4; + buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; + buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; + break; } } @@ -1048,6 +1205,14 @@ unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line) case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: return buf_line & 1; default: return 0; @@ -1395,42 +1560,10 @@ static void tpg_precalculate_line(struct tpg_data *tpg) /* need this to do rgb24 rendering */ typedef struct { u16 __; u8 _; } __packed x24; -void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], - int y, int x, char *text) -{ - int line; - unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; - unsigned div = step; - unsigned first = 0; - unsigned len = strlen(text); - unsigned p; - - if (font8x16 == NULL || basep == NULL) - return; - - /* Checks if it is possible to show string */ - if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) - return; - - if (len > (tpg->compose.width - x) / 8) - len = (tpg->compose.width - x) / 8; - if (tpg->vflip) - y = tpg->compose.height - y - 16; - if (tpg->hflip) - x = tpg->compose.width - x - 8; - y += tpg->compose.top; - x += tpg->compose.left; - if (tpg->field == V4L2_FIELD_BOTTOM) - first = 1; - else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT) - div = 2; - - for (p = 0; p < tpg->planes; p++) { - unsigned vdiv = tpg->vdownsampling[p]; - unsigned hdiv = tpg->hdownsampling[p]; - - /* Print text */ #define PRINTSTR(PIXTYPE) do { \ + unsigned vdiv = tpg->vdownsampling[p]; \ + unsigned hdiv = tpg->hdownsampling[p]; \ + int line; \ PIXTYPE fg; \ PIXTYPE bg; \ memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \ @@ -1481,15 +1614,82 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], } \ } while (0) +static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + unsigned p, unsigned first, unsigned div, unsigned step, + int y, int x, char *text, unsigned len) +{ + PRINTSTR(u8); +} + +static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + unsigned p, unsigned first, unsigned div, unsigned step, + int y, int x, char *text, unsigned len) +{ + PRINTSTR(u16); +} + +static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + unsigned p, unsigned first, unsigned div, unsigned step, + int y, int x, char *text, unsigned len) +{ + PRINTSTR(x24); +} + +static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + unsigned p, unsigned first, unsigned div, unsigned step, + int y, int x, char *text, unsigned len) +{ + PRINTSTR(u32); +} + +void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], + int y, int x, char *text) +{ + unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; + unsigned div = step; + unsigned first = 0; + unsigned len = strlen(text); + unsigned p; + + if (font8x16 == NULL || basep == NULL) + return; + + /* Checks if it is possible to show string */ + if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) + return; + + if (len > (tpg->compose.width - x) / 8) + len = (tpg->compose.width - x) / 8; + if (tpg->vflip) + y = tpg->compose.height - y - 16; + if (tpg->hflip) + x = tpg->compose.width - x - 8; + y += tpg->compose.top; + x += tpg->compose.left; + if (tpg->field == V4L2_FIELD_BOTTOM) + first = 1; + else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT) + div = 2; + + for (p = 0; p < tpg->planes; p++) { + /* Print text */ switch (tpg->twopixelsize[p]) { case 2: - PRINTSTR(u8); break; + tpg_print_str_2(tpg, basep, p, first, div, step, y, x, + text, len); + break; case 4: - PRINTSTR(u16); break; + tpg_print_str_4(tpg, basep, p, first, div, step, y, x, + text, len); + break; case 6: - PRINTSTR(x24); break; + tpg_print_str_6(tpg, basep, p, first, div, step, y, x, + text, len); + break; case 8: - PRINTSTR(u32); break; + tpg_print_str_8(tpg, basep, p, first, div, step, y, x, + text, len); + break; } } } @@ -1583,50 +1783,23 @@ static void tpg_recalc(struct tpg_data *tpg) if (tpg->recalc_colors) { tpg->recalc_colors = false; tpg->recalc_lines = true; + tpg->real_xfer_func = tpg->xfer_func; tpg->real_ycbcr_enc = tpg->ycbcr_enc; tpg->real_quantization = tpg->quantization; - if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) { - switch (tpg->colorspace) { - case V4L2_COLORSPACE_REC709: - tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_709; - break; - case V4L2_COLORSPACE_SRGB: - tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SYCC; - break; - case V4L2_COLORSPACE_BT2020: - tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_BT2020; - break; - case V4L2_COLORSPACE_SMPTE240M: - tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SMPTE240M; - break; - case V4L2_COLORSPACE_SMPTE170M: - case V4L2_COLORSPACE_470_SYSTEM_M: - case V4L2_COLORSPACE_470_SYSTEM_BG: - case V4L2_COLORSPACE_ADOBERGB: - default: - tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_601; - break; - } - } - if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) { - tpg->real_quantization = V4L2_QUANTIZATION_FULL_RANGE; - if (tpg->is_yuv) { - switch (tpg->real_ycbcr_enc) { - case V4L2_YCBCR_ENC_SYCC: - case V4L2_YCBCR_ENC_XV601: - case V4L2_YCBCR_ENC_XV709: - break; - default: - tpg->real_quantization = - V4L2_QUANTIZATION_LIM_RANGE; - break; - } - } else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) { - /* R'G'B' BT.2020 is limited range */ - tpg->real_quantization = - V4L2_QUANTIZATION_LIM_RANGE; - } - } + + if (tpg->xfer_func == V4L2_XFER_FUNC_DEFAULT) + tpg->real_xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(tpg->colorspace); + + if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + tpg->real_ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(tpg->colorspace); + + if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) + tpg->real_quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(!tpg->is_yuv, + tpg->colorspace, tpg->real_ycbcr_enc); + tpg_precalculate_colors(tpg); } if (tpg->recalc_square_border) { @@ -1670,6 +1843,23 @@ static int tpg_pattern_avg(const struct tpg_data *tpg, return -1; } +void tpg_log_status(struct tpg_data *tpg) +{ + pr_info("tpg source WxH: %ux%u (%s)\n", + tpg->src_width, tpg->src_height, + tpg->is_yuv ? "YCbCr" : "RGB"); + pr_info("tpg field: %u\n", tpg->field); + pr_info("tpg crop: %ux%u@%dx%d\n", tpg->crop.width, tpg->crop.height, + tpg->crop.left, tpg->crop.top); + pr_info("tpg compose: %ux%u@%dx%d\n", tpg->compose.width, tpg->compose.height, + tpg->compose.left, tpg->compose.top); + pr_info("tpg colorspace: %d\n", tpg->colorspace); + pr_info("tpg transfer function: %d/%d\n", tpg->xfer_func, tpg->real_xfer_func); + pr_info("tpg Y'CbCr encoding: %d/%d\n", tpg->ycbcr_enc, tpg->real_ycbcr_enc); + pr_info("tpg quantization: %d/%d\n", tpg->quantization, tpg->real_quantization); + pr_info("tpg RGB range: %d/%d\n", tpg->rgb_range, tpg->real_rgb_range); +} + /* * This struct contains common parameters used by both the drawing of the * test pattern and the drawing of the extras (borders, square, etc.) diff --git a/kernel/drivers/media/platform/vivid/vivid-tpg.h b/kernel/drivers/media/platform/vivid/vivid-tpg.h index a50cd2e25..9baed6a10 100644 --- a/kernel/drivers/media/platform/vivid/vivid-tpg.h +++ b/kernel/drivers/media/platform/vivid/vivid-tpg.h @@ -122,7 +122,13 @@ struct tpg_data { u32 fourcc; bool is_yuv; u32 colorspace; + u32 xfer_func; u32 ycbcr_enc; + /* + * Stores the actual transfer function, i.e. will never be + * V4L2_XFER_FUNC_DEFAULT. + */ + u32 real_xfer_func; /* * Stores the actual Y'CbCr encoding, i.e. will never be * V4L2_YCBCR_ENC_DEFAULT. @@ -192,6 +198,7 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w); void tpg_free(struct tpg_data *tpg); void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, u32 field); +void tpg_log_status(struct tpg_data *tpg); void tpg_set_font(const u8 *f); void tpg_gen_text(const struct tpg_data *tpg, @@ -328,6 +335,19 @@ static inline u32 tpg_g_ycbcr_enc(const struct tpg_data *tpg) return tpg->ycbcr_enc; } +static inline void tpg_s_xfer_func(struct tpg_data *tpg, u32 xfer_func) +{ + if (tpg->xfer_func == xfer_func) + return; + tpg->xfer_func = xfer_func; + tpg->recalc_colors = true; +} + +static inline u32 tpg_g_xfer_func(const struct tpg_data *tpg) +{ + return tpg->xfer_func; +} + static inline void tpg_s_quantization(struct tpg_data *tpg, u32 quantization) { if (tpg->quantization == quantization) diff --git a/kernel/drivers/media/platform/vivid/vivid-vbi-cap.c b/kernel/drivers/media/platform/vivid/vivid-vbi-cap.c index ef81b01b5..e903d023e 100644 --- a/kernel/drivers/media/platform/vivid/vivid-vbi-cap.c +++ b/kernel/drivers/media/platform/vivid/vivid-vbi-cap.c @@ -94,36 +94,38 @@ static void vivid_g_fmt_vbi_cap(struct vivid_dev *dev, struct v4l2_vbi_format *v void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) { struct v4l2_vbi_format vbi; - u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0); + u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); vivid_g_fmt_vbi_cap(dev, &vbi); - buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count; + buf->vb.sequence = dev->vbi_cap_seq_count; if (dev->field_cap == V4L2_FIELD_ALTERNATE) - buf->vb.v4l2_buf.sequence /= 2; + buf->vb.sequence /= 2; - vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence); + vivid_sliced_vbi_cap_fill(dev, buf->vb.sequence); - memset(vbuf, 0x10, vb2_plane_size(&buf->vb, 0)); + memset(vbuf, 0x10, vb2_plane_size(&buf->vb.vb2_buf, 0)); if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.timestamp.tv_sec += dev->time_wrap_offset; } -void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) +void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, + struct vivid_buffer *buf) { - struct v4l2_sliced_vbi_data *vbuf = vb2_plane_vaddr(&buf->vb, 0); + struct v4l2_sliced_vbi_data *vbuf = + vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - buf->vb.v4l2_buf.sequence = dev->vbi_cap_seq_count; + buf->vb.sequence = dev->vbi_cap_seq_count; if (dev->field_cap == V4L2_FIELD_ALTERNATE) - buf->vb.v4l2_buf.sequence /= 2; + buf->vb.sequence /= 2; - vivid_sliced_vbi_cap_fill(dev, buf->vb.v4l2_buf.sequence); + vivid_sliced_vbi_cap_fill(dev, buf->vb.sequence); - memset(vbuf, 0, vb2_plane_size(&buf->vb, 0)); + memset(vbuf, 0, vb2_plane_size(&buf->vb.vb2_buf, 0)); if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) { unsigned i; @@ -131,11 +133,11 @@ void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *bu vbuf[i] = dev->vbi_gen.data[i]; } - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.timestamp.tv_sec += dev->time_wrap_offset; + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.timestamp.tv_sec += dev->time_wrap_offset; } -static int vbi_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vbi_cap_queue_setup(struct vb2_queue *vq, const void *parg, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], void *alloc_ctxs[]) { @@ -187,8 +189,9 @@ static int vbi_cap_buf_prepare(struct vb2_buffer *vb) static void vbi_cap_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -215,7 +218,8 @@ static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count) list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } return err; diff --git a/kernel/drivers/media/platform/vivid/vivid-vbi-out.c b/kernel/drivers/media/platform/vivid/vivid-vbi-out.c index 4e4c70e1e..75c5709f9 100644 --- a/kernel/drivers/media/platform/vivid/vivid-vbi-out.c +++ b/kernel/drivers/media/platform/vivid/vivid-vbi-out.c @@ -27,7 +27,7 @@ #include "vivid-vbi-out.h" #include "vivid-vbi-cap.h" -static int vbi_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vbi_out_queue_setup(struct vb2_queue *vq, const void *parg, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], void *alloc_ctxs[]) { @@ -79,8 +79,9 @@ static int vbi_out_buf_prepare(struct vb2_buffer *vb) static void vbi_out_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -107,7 +108,8 @@ static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count) list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } return err; @@ -201,7 +203,8 @@ int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_forma return 0; } -int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, + struct v4l2_format *fmt) { struct vivid_dev *dev = video_drvdata(file); struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; @@ -217,10 +220,13 @@ int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format return 0; } -void vivid_sliced_vbi_out_process(struct vivid_dev *dev, struct vivid_buffer *buf) +void vivid_sliced_vbi_out_process(struct vivid_dev *dev, + struct vivid_buffer *buf) { - struct v4l2_sliced_vbi_data *vbi = vb2_plane_vaddr(&buf->vb, 0); - unsigned elems = vb2_get_plane_payload(&buf->vb, 0) / sizeof(*vbi); + struct v4l2_sliced_vbi_data *vbi = + vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + unsigned elems = + vb2_get_plane_payload(&buf->vb.vb2_buf, 0) / sizeof(*vbi); dev->vbi_out_have_cc[0] = false; dev->vbi_out_have_cc[1] = false; diff --git a/kernel/drivers/media/platform/vivid/vivid-vid-cap.c b/kernel/drivers/media/platform/vivid/vivid-vid-cap.c index dab5990f4..ef5412311 100644 --- a/kernel/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/kernel/drivers/media/platform/vivid/vivid-vid-cap.c @@ -40,7 +40,6 @@ static const struct v4l2_fract static const struct vivid_fmt formats_ovl[] = { { - .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -48,7 +47,6 @@ static const struct vivid_fmt formats_ovl[] = { .buffers = 1, }, { - .name = "XRGB555 (LE)", .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb arrrrrgg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -56,7 +54,6 @@ static const struct vivid_fmt formats_ovl[] = { .buffers = 1, }, { - .name = "ARGB555 (LE)", .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -66,7 +63,7 @@ static const struct vivid_fmt formats_ovl[] = { }; /* The number of discrete webcam framesizes */ -#define VIVID_WEBCAM_SIZES 3 +#define VIVID_WEBCAM_SIZES 4 /* The number of discrete webcam frameintervals */ #define VIVID_WEBCAM_IVALS (VIVID_WEBCAM_SIZES * 2) @@ -75,6 +72,7 @@ static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = { { 320, 180 }, { 640, 360 }, { 1280, 720 }, + { 1920, 1080 }, }; /* @@ -82,6 +80,8 @@ static const struct v4l2_frmsize_discrete webcam_sizes[VIVID_WEBCAM_SIZES] = { * elements in this array as there are in webcam_sizes. */ static const struct v4l2_fract webcam_intervals[VIVID_WEBCAM_IVALS] = { + { 1, 2 }, + { 1, 5 }, { 1, 10 }, { 1, 15 }, { 1, 25 }, @@ -95,10 +95,11 @@ static const struct v4l2_discrete_probe webcam_probe = { VIVID_WEBCAM_SIZES }; -static int vid_cap_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vid_cap_queue_setup(struct vb2_queue *vq, const void *parg, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct vivid_dev *dev = vb2_get_drv_priv(vq); unsigned buffers = tpg_g_buffers(&dev->tpg); unsigned h = dev->fmt_cap_rect.height; @@ -198,7 +199,7 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) } vb2_set_plane_payload(vb, p, size); - vb->v4l2_planes[p].data_offset = dev->fmt_cap->data_offset[p]; + vb->planes[p].data_offset = dev->fmt_cap->data_offset[p]; } return 0; @@ -206,10 +207,11 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) static void vid_cap_buf_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct v4l2_timecode *tc = &vb->v4l2_buf.timecode; + struct v4l2_timecode *tc = &vbuf->timecode; unsigned fps = 25; - unsigned seq = vb->v4l2_buf.sequence; + unsigned seq = vbuf->sequence; if (!vivid_is_sdtv_cap(dev)) return; @@ -218,7 +220,7 @@ static void vid_cap_buf_finish(struct vb2_buffer *vb) * Set the timecode. Rarely used, so it is interesting to * test this. */ - vb->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMECODE; + vbuf->flags |= V4L2_BUF_FLAG_TIMECODE; if (dev->std_cap & V4L2_STD_525_60) fps = 30; tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS; @@ -231,8 +233,9 @@ static void vid_cap_buf_finish(struct vb2_buffer *vb) static void vid_cap_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -268,7 +271,8 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } return err; @@ -501,6 +505,13 @@ static unsigned vivid_colorspace_cap(struct vivid_dev *dev) return dev->colorspace_out; } +static unsigned vivid_xfer_func_cap(struct vivid_dev *dev) +{ + if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + return tpg_g_xfer_func(&dev->tpg); + return dev->xfer_func_out; +} + static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) { if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) @@ -527,6 +538,7 @@ int vivid_g_fmt_vid_cap(struct file *file, void *priv, mp->field = dev->field_cap; mp->pixelformat = dev->fmt_cap->fourcc; mp->colorspace = vivid_colorspace_cap(dev); + mp->xfer_func = vivid_xfer_func_cap(dev); mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); mp->quantization = vivid_quantization_cap(dev); mp->num_planes = dev->fmt_cap->buffers; @@ -616,6 +628,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, } mp->colorspace = vivid_colorspace_cap(dev); mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); + mp->xfer_func = vivid_xfer_func_cap(dev); mp->quantization = vivid_quantization_cap(dev); memset(mp->reserved, 0, sizeof(mp->reserved)); return 0; @@ -720,8 +733,8 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, webcam_sizes[i].height == mp->height) break; dev->webcam_size_idx = i; - if (dev->webcam_ival_idx >= 2 * (3 - i)) - dev->webcam_ival_idx = 2 * (3 - i) - 1; + if (dev->webcam_ival_idx >= 2 * (VIVID_WEBCAM_SIZES - i)) + dev->webcam_ival_idx = 2 * (VIVID_WEBCAM_SIZES - i) - 1; vivid_update_format_cap(dev, false); } else { struct v4l2_rect r = { 0, 0, mp->width, mp->height }; @@ -1030,7 +1043,6 @@ int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, fmt = &formats_ovl[f->index]; - strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; } @@ -1619,8 +1631,8 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) h_freq = (u32)bt->pixelclock / total_h_pixel; if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { - if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, - bt->polarities, timings)) + if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width, + bt->polarities, bt->interlaced, timings)) return true; } @@ -1631,7 +1643,8 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) &aspect_ratio.numerator, &aspect_ratio.denominator); if (v4l2_detect_gtf(total_v_lines, h_freq, bt->vsync, - bt->polarities, aspect_ratio, timings)) + bt->polarities, bt->interlaced, + aspect_ratio, timings)) return true; } return false; @@ -1768,7 +1781,7 @@ int vidioc_enum_frameintervals(struct file *file, void *priv, break; if (i == ARRAY_SIZE(webcam_sizes)) return -EINVAL; - if (fival->index >= 2 * (3 - i)) + if (fival->index >= 2 * (VIVID_WEBCAM_SIZES - i)) return -EINVAL; fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; fival->discrete = webcam_intervals[fival->index]; @@ -1798,7 +1811,7 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { struct vivid_dev *dev = video_drvdata(file); - unsigned ival_sz = 2 * (3 - dev->webcam_size_idx); + unsigned ival_sz = 2 * (VIVID_WEBCAM_SIZES - dev->webcam_size_idx); struct v4l2_fract tpf; unsigned i; diff --git a/kernel/drivers/media/platform/vivid/vivid-vid-common.c b/kernel/drivers/media/platform/vivid/vivid-vid-common.c index aa446271a..1678b730d 100644 --- a/kernel/drivers/media/platform/vivid/vivid-vid-common.c +++ b/kernel/drivers/media/platform/vivid/vivid-vid-common.c @@ -45,7 +45,6 @@ const struct v4l2_dv_timings_cap vivid_dv_timings_cap = { struct vivid_fmt vivid_formats[] = { { - .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -55,7 +54,6 @@ struct vivid_fmt vivid_formats[] = { .data_offset = { PLANE0_DATA_OFFSET }, }, { - .name = "4:2:2, packed, UYVY", .fourcc = V4L2_PIX_FMT_UYVY, .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -64,7 +62,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "4:2:2, packed, YVYU", .fourcc = V4L2_PIX_FMT_YVYU, .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -73,7 +70,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "4:2:2, packed, VYUY", .fourcc = V4L2_PIX_FMT_VYUY, .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -82,7 +78,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV 4:2:2 triplanar", .fourcc = V4L2_PIX_FMT_YUV422P, .vdownsampling = { 1, 1, 1 }, .bit_depth = { 8, 4, 4 }, @@ -91,7 +86,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV 4:2:0 triplanar", .fourcc = V4L2_PIX_FMT_YUV420, .vdownsampling = { 1, 2, 2 }, .bit_depth = { 8, 4, 4 }, @@ -100,7 +94,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YVU 4:2:0 triplanar", .fourcc = V4L2_PIX_FMT_YVU420, .vdownsampling = { 1, 2, 2 }, .bit_depth = { 8, 4, 4 }, @@ -109,7 +102,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV 4:2:0 biplanar", .fourcc = V4L2_PIX_FMT_NV12, .vdownsampling = { 1, 2 }, .bit_depth = { 8, 8 }, @@ -118,7 +110,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YVU 4:2:0 biplanar", .fourcc = V4L2_PIX_FMT_NV21, .vdownsampling = { 1, 2 }, .bit_depth = { 8, 8 }, @@ -127,7 +118,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV 4:2:2 biplanar", .fourcc = V4L2_PIX_FMT_NV16, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 8 }, @@ -136,7 +126,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YVU 4:2:2 biplanar", .fourcc = V4L2_PIX_FMT_NV61, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 8 }, @@ -145,7 +134,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV 4:4:4 biplanar", .fourcc = V4L2_PIX_FMT_NV24, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 16 }, @@ -154,7 +142,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YVU 4:4:4 biplanar", .fourcc = V4L2_PIX_FMT_NV42, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 16 }, @@ -163,7 +150,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV555 (LE)", .fourcc = V4L2_PIX_FMT_YUV555, /* uuuvvvvv ayyyyyuu */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -172,7 +158,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x8000, }, { - .name = "YUV565 (LE)", .fourcc = V4L2_PIX_FMT_YUV565, /* uuuvvvvv yyyyyuuu */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -180,7 +165,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "YUV444", .fourcc = V4L2_PIX_FMT_YUV444, /* uuuuvvvv aaaayyyy */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -189,7 +173,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0xf000, }, { - .name = "YUV32 (LE)", .fourcc = V4L2_PIX_FMT_YUV32, /* ayuv */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -198,7 +181,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x000000ff, }, { - .name = "Monochrome", .fourcc = V4L2_PIX_FMT_GREY, .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -207,7 +189,22 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "RGB332", + .fourcc = V4L2_PIX_FMT_Y16, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .is_yuv = true, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_Y16_BE, + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .is_yuv = true, + .planes = 1, + .buffers = 1, + }, + { .fourcc = V4L2_PIX_FMT_RGB332, /* rrrgggbb */ .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -215,7 +212,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -224,7 +220,6 @@ struct vivid_fmt vivid_formats[] = { .can_do_overlay = true, }, { - .name = "RGB565 (BE)", .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -233,7 +228,6 @@ struct vivid_fmt vivid_formats[] = { .can_do_overlay = true, }, { - .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444, /* xxxxrrrr ggggbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -241,7 +235,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "XRGB444", .fourcc = V4L2_PIX_FMT_XRGB444, /* xxxxrrrr ggggbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -249,7 +242,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "ARGB444", .fourcc = V4L2_PIX_FMT_ARGB444, /* aaaarrrr ggggbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -258,7 +250,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x00f0, }, { - .name = "RGB555 (LE)", .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb xrrrrrgg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -267,7 +258,6 @@ struct vivid_fmt vivid_formats[] = { .can_do_overlay = true, }, { - .name = "XRGB555 (LE)", .fourcc = V4L2_PIX_FMT_XRGB555, /* gggbbbbb xrrrrrgg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -276,7 +266,6 @@ struct vivid_fmt vivid_formats[] = { .can_do_overlay = true, }, { - .name = "ARGB555 (LE)", .fourcc = V4L2_PIX_FMT_ARGB555, /* gggbbbbb arrrrrgg */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -286,7 +275,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x8000, }, { - .name = "RGB555 (BE)", .fourcc = V4L2_PIX_FMT_RGB555X, /* xrrrrrgg gggbbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -294,7 +282,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "XRGB555 (BE)", .fourcc = V4L2_PIX_FMT_XRGB555X, /* xrrrrrgg gggbbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -302,7 +289,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "ARGB555 (BE)", .fourcc = V4L2_PIX_FMT_ARGB555X, /* arrrrrgg gggbbbbb */ .vdownsampling = { 1 }, .bit_depth = { 16 }, @@ -311,7 +297,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x0080, }, { - .name = "RGB24 (LE)", .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ .vdownsampling = { 1 }, .bit_depth = { 24 }, @@ -319,7 +304,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "RGB24 (BE)", .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ .vdownsampling = { 1 }, .bit_depth = { 24 }, @@ -327,7 +311,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666, /* bbbbbbgg ggggrrrr rrxxxxxx */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -335,7 +318,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "RGB32 (LE)", .fourcc = V4L2_PIX_FMT_RGB32, /* xrgb */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -343,7 +325,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "RGB32 (BE)", .fourcc = V4L2_PIX_FMT_BGR32, /* bgrx */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -351,7 +332,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "XRGB32 (LE)", .fourcc = V4L2_PIX_FMT_XRGB32, /* xrgb */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -359,7 +339,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "XRGB32 (BE)", .fourcc = V4L2_PIX_FMT_XBGR32, /* bgrx */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -367,7 +346,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "ARGB32 (LE)", .fourcc = V4L2_PIX_FMT_ARGB32, /* argb */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -376,7 +354,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0x000000ff, }, { - .name = "ARGB32 (BE)", .fourcc = V4L2_PIX_FMT_ABGR32, /* bgra */ .vdownsampling = { 1 }, .bit_depth = { 32 }, @@ -385,7 +362,6 @@ struct vivid_fmt vivid_formats[] = { .alpha_mask = 0xff000000, }, { - .name = "Bayer BG/GR", .fourcc = V4L2_PIX_FMT_SBGGR8, /* Bayer BG/GR */ .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -393,7 +369,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "Bayer GB/RG", .fourcc = V4L2_PIX_FMT_SGBRG8, /* Bayer GB/RG */ .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -401,7 +376,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "Bayer GR/BG", .fourcc = V4L2_PIX_FMT_SGRBG8, /* Bayer GR/BG */ .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -409,7 +383,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "Bayer RG/GB", .fourcc = V4L2_PIX_FMT_SRGGB8, /* Bayer RG/GB */ .vdownsampling = { 1 }, .bit_depth = { 8 }, @@ -417,7 +390,62 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { - .name = "4:2:2, biplanar, YUV", + .fourcc = V4L2_PIX_FMT_SBGGR10, /* Bayer BG/GR */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, /* Bayer GB/RG */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, /* Bayer GR/BG */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, /* Bayer RG/GB */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, /* Bayer BG/GR */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, /* Bayer GB/RG */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, /* Bayer GR/BG */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, /* Bayer RG/GB */ + .vdownsampling = { 1 }, + .bit_depth = { 16 }, + .planes = 1, + .buffers = 1, + }, + { .fourcc = V4L2_PIX_FMT_NV16M, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 8 }, @@ -427,7 +455,6 @@ struct vivid_fmt vivid_formats[] = { .data_offset = { PLANE0_DATA_OFFSET, 0 }, }, { - .name = "4:2:2, biplanar, YVU", .fourcc = V4L2_PIX_FMT_NV61M, .vdownsampling = { 1, 1 }, .bit_depth = { 8, 8 }, @@ -437,7 +464,6 @@ struct vivid_fmt vivid_formats[] = { .data_offset = { 0, PLANE0_DATA_OFFSET }, }, { - .name = "4:2:0, triplanar, YUV", .fourcc = V4L2_PIX_FMT_YUV420M, .vdownsampling = { 1, 2, 2 }, .bit_depth = { 8, 4, 4 }, @@ -446,7 +472,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 3, }, { - .name = "4:2:0, triplanar, YVU", .fourcc = V4L2_PIX_FMT_YVU420M, .vdownsampling = { 1, 2, 2 }, .bit_depth = { 8, 4, 4 }, @@ -455,7 +480,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 3, }, { - .name = "4:2:0, biplanar, YUV", .fourcc = V4L2_PIX_FMT_NV12M, .vdownsampling = { 1, 2 }, .bit_depth = { 8, 8 }, @@ -464,7 +488,6 @@ struct vivid_fmt vivid_formats[] = { .buffers = 2, }, { - .name = "4:2:0, biplanar, YVU", .fourcc = V4L2_PIX_FMT_NV21M, .vdownsampling = { 1, 2 }, .bit_depth = { 8, 8 }, @@ -557,6 +580,7 @@ void fmt_sp2mp(const struct v4l2_format *sp_fmt, struct v4l2_format *mp_fmt) mp->pixelformat = pix->pixelformat; mp->field = pix->field; mp->colorspace = pix->colorspace; + mp->xfer_func = pix->xfer_func; mp->ycbcr_enc = pix->ycbcr_enc; mp->quantization = pix->quantization; mp->num_planes = 1; @@ -585,6 +609,7 @@ int fmt_sp2mp_func(struct file *file, void *priv, pix->pixelformat = mp->pixelformat; pix->field = mp->field; pix->colorspace = mp->colorspace; + pix->xfer_func = mp->xfer_func; pix->ycbcr_enc = mp->ycbcr_enc; pix->quantization = mp->quantization; pix->sizeimage = ppix->sizeimage; @@ -750,7 +775,6 @@ int vivid_enum_fmt_vid(struct file *file, void *priv, fmt = &vivid_formats[f->index]; - strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; } diff --git a/kernel/drivers/media/platform/vivid/vivid-vid-out.c b/kernel/drivers/media/platform/vivid/vivid-vid-out.c index 0af43dc77..b77acb6a7 100644 --- a/kernel/drivers/media/platform/vivid/vivid-vid-out.c +++ b/kernel/drivers/media/platform/vivid/vivid-vid-out.c @@ -31,10 +31,11 @@ #include "vivid-kthread-out.h" #include "vivid-vid-out.h" -static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vid_out_queue_setup(struct vb2_queue *vq, const void *parg, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct vivid_dev *dev = vb2_get_drv_priv(vq); const struct vivid_fmt *vfmt = dev->fmt_out; unsigned planes = vfmt->buffers; @@ -109,6 +110,7 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *f static int vid_out_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size; unsigned planes; @@ -131,14 +133,14 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) } if (dev->field_out != V4L2_FIELD_ALTERNATE) - vb->v4l2_buf.field = dev->field_out; - else if (vb->v4l2_buf.field != V4L2_FIELD_TOP && - vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) + vbuf->field = dev->field_out; + else if (vbuf->field != V4L2_FIELD_TOP && + vbuf->field != V4L2_FIELD_BOTTOM) return -EINVAL; for (p = 0; p < planes; p++) { size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + - vb->v4l2_planes[p].data_offset; + vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n", @@ -152,8 +154,9 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) static void vid_out_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct vivid_buffer *buf = container_of(vb, struct vivid_buffer, vb); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -186,7 +189,8 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } return err; @@ -258,6 +262,7 @@ void vivid_update_format_out(struct vivid_dev *dev) } break; } + dev->xfer_func_out = V4L2_XFER_FUNC_DEFAULT; dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT; dev->quantization_out = V4L2_QUANTIZATION_DEFAULT; dev->compose_out = dev->sink_rect; @@ -320,6 +325,7 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, mp->field = dev->field_out; mp->pixelformat = fmt->fourcc; mp->colorspace = dev->colorspace_out; + mp->xfer_func = dev->xfer_func_out; mp->ycbcr_enc = dev->ycbcr_enc_out; mp->quantization = dev->quantization_out; mp->num_planes = fmt->buffers; @@ -407,6 +413,7 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, for (p = fmt->buffers; p < fmt->planes; p++) pfmt[0].sizeimage += (pfmt[0].bytesperline * fmt->bit_depth[p]) / (fmt->bit_depth[0] * fmt->vdownsampling[p]); + mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; mp->quantization = V4L2_QUANTIZATION_DEFAULT; if (vivid_is_svid_out(dev)) { @@ -546,6 +553,7 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv, set_colorspace: dev->colorspace_out = mp->colorspace; + dev->xfer_func_out = mp->xfer_func; dev->ycbcr_enc_out = mp->ycbcr_enc; dev->quantization_out = mp->quantization; if (dev->loop_video) { @@ -1120,15 +1128,26 @@ int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id) return 0; } +static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + + if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) && + v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL)) + return true; + + return false; +} + int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings) { struct vivid_dev *dev = video_drvdata(file); - if (!vivid_is_hdmi_out(dev)) return -ENODATA; if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, - 0, NULL, NULL)) + 0, NULL, NULL) && + !valid_cvt_gtf_timings(timings)) return -EINVAL; if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0)) return 0; @@ -1152,7 +1171,8 @@ int vivid_vid_out_g_parm(struct file *file, void *priv, parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.output.timeperframe = dev->timeperframe_vid_out; parm->parm.output.writebuffers = 1; -return 0; + + return 0; } int vidioc_subscribe_event(struct v4l2_fh *fh, diff --git a/kernel/drivers/media/platform/vsp1/vsp1_drv.c b/kernel/drivers/media/platform/vsp1/vsp1_drv.c index 913485a90..4e6188638 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_drv.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_drv.c @@ -1,7 +1,7 @@ /* * vsp1_drv.c -- R-Car VSP1 Driver * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -403,7 +403,10 @@ static int vsp1_pm_suspend(struct device *dev) if (vsp1->ref_count == 0) return 0; + vsp1_pipelines_suspend(vsp1); + clk_disable_unprepare(vsp1->clock); + return 0; } @@ -413,10 +416,14 @@ static int vsp1_pm_resume(struct device *dev) WARN_ON(mutex_is_locked(&vsp1->lock)); - if (vsp1->ref_count) + if (vsp1->ref_count == 0) return 0; - return clk_prepare_enable(vsp1->clock); + clk_prepare_enable(vsp1->clock); + + vsp1_pipelines_resume(vsp1); + + return 0; } #endif diff --git a/kernel/drivers/media/platform/vsp1/vsp1_entity.c b/kernel/drivers/media/platform/vsp1/vsp1_entity.c index a453bb4dd..fd95a75b0 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_entity.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_entity.c @@ -24,22 +24,24 @@ bool vsp1_entity_is_streaming(struct vsp1_entity *entity) { + unsigned long flags; bool streaming; - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); streaming = entity->streaming; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); return streaming; } int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) { + unsigned long flags; int ret; - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); entity->streaming = streaming; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); if (!streaming) return 0; @@ -49,9 +51,9 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); if (ret < 0) { - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); entity->streaming = false; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); } return ret; @@ -193,7 +195,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; - mutex_init(&entity->lock); + spin_lock_init(&entity->lock); entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; @@ -228,6 +230,4 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); - - mutex_destroy(&entity->lock); } diff --git a/kernel/drivers/media/platform/vsp1/vsp1_entity.h b/kernel/drivers/media/platform/vsp1/vsp1_entity.h index 62c768d1c..8867a5787 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_entity.h +++ b/kernel/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,7 +14,7 @@ #define __VSP1_ENTITY_H__ #include -#include +#include #include @@ -73,7 +73,7 @@ struct vsp1_entity { struct vsp1_video *video; - struct mutex lock; /* Protects the streaming field */ + spinlock_t lock; /* Protects the streaming field */ bool streaming; }; diff --git a/kernel/drivers/media/platform/vsp1/vsp1_regs.h b/kernel/drivers/media/platform/vsp1/vsp1_regs.h index da3c573e1..25b48738b 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_regs.h +++ b/kernel/drivers/media/platform/vsp1/vsp1_regs.h @@ -238,7 +238,7 @@ #define VI6_WPF_SZCLIP_EN (1 << 28) #define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16) #define VI6_WPF_SZCLIP_OFST_SHIFT 16 -#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0) +#define VI6_WPF_SZCLIP_SIZE_MASK (0xfff << 0) #define VI6_WPF_SZCLIP_SIZE_SHIFT 0 #define VI6_WPF_OUTFMT 0x100c @@ -304,9 +304,9 @@ #define VI6_DPR_HST_ROUTE 0x2044 #define VI6_DPR_HSI_ROUTE 0x2048 #define VI6_DPR_BRU_ROUTE 0x204c -#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FXA_MASK (0xff << 16) #define VI6_DPR_ROUTE_FXA_SHIFT 16 -#define VI6_DPR_ROUTE_FP_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FP_MASK (0x3f << 8) #define VI6_DPR_ROUTE_FP_SHIFT 8 #define VI6_DPR_ROUTE_RT_MASK (0x3f << 0) #define VI6_DPR_ROUTE_RT_SHIFT 0 diff --git a/kernel/drivers/media/platform/vsp1/vsp1_rpf.c b/kernel/drivers/media/platform/vsp1/vsp1_rpf.c index 3294529a3..cd5248a9a 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_rpf.c @@ -200,10 +200,10 @@ static void rpf_vdev_queue(struct vsp1_video *video, vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0] + rpf->offsets[0]); - if (buf->buf.num_planes > 1) + if (buf->buf.vb2_buf.num_planes > 1) vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1] + rpf->offsets[1]); - if (buf->buf.num_planes > 2) + if (buf->buf.vb2_buf.num_planes > 2) vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2] + rpf->offsets[1]); } diff --git a/kernel/drivers/media/platform/vsp1/vsp1_rwpf.c b/kernel/drivers/media/platform/vsp1/vsp1_rwpf.c index fa71f4695..9688c219b 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -197,6 +197,17 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, */ format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); + + /* Restrict the crop rectangle coordinates to multiples of 2 to avoid + * shifting the color plane. + */ + if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + sel->r.left = ALIGN(sel->r.left, 2); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = round_down(sel->r.width, 2); + sel->r.height = round_down(sel->r.height, 2); + } + sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); if (rwpf->entity.type == VSP1_ENTITY_WPF) { diff --git a/kernel/drivers/media/platform/vsp1/vsp1_video.c b/kernel/drivers/media/platform/vsp1/vsp1_video.c index d91f19a9e..b4f8cd74e 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_video.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_video.c @@ -1,7 +1,7 @@ /* * vsp1_video.c -- R-Car VSP1 Video Node * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "vsp1.h" @@ -245,7 +245,7 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * the datasheet, strides not aligned to a multiple of 128 bytes result * in image corruption. */ - for (i = 0; i < max(info->planes, 2U); ++i) { + for (i = 0; i < min(info->planes, 2U); ++i) { unsigned int hsub = i > 0 ? info->hsub : 1; unsigned int vsub = i > 0 ? info->vsub : 1; unsigned int align = 128; @@ -514,6 +514,18 @@ static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) pipe->buffers_ready = 0; } +static bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + bool stopped; + + spin_lock_irqsave(&pipe->irqlock, flags); + stopped = pipe->state == VSP1_PIPELINE_STOPPED; + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return stopped; +} + static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -525,7 +537,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPING; spin_unlock_irqrestore(&pipe->irqlock, flags); - ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); ret = ret == 0 ? -ETIMEDOUT : 0; @@ -598,11 +610,11 @@ vsp1_video_complete_buffer(struct vsp1_video *video) spin_unlock_irqrestore(&video->irqlock, flags); - done->buf.v4l2_buf.sequence = video->sequence++; - v4l2_get_timestamp(&done->buf.v4l2_buf.timestamp); - for (i = 0; i < done->buf.num_planes; ++i) - vb2_set_plane_payload(&done->buf, i, done->length[i]); - vb2_buffer_done(&done->buf, VB2_BUF_STATE_DONE); + done->buf.sequence = video->sequence++; + v4l2_get_timestamp(&done->buf.timestamp); + for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) + vb2_set_plane_payload(&done->buf.vb2_buf, i, done->length[i]); + vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); return next; } @@ -703,15 +715,83 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, } } +void vsp1_pipelines_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and then + * to wait for the stop to complete. + */ + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_pipelines_resume(struct vsp1_device *vsp1) +{ + unsigned int i; + + /* Resume pipeline all running pipelines. */ + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ static int -vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +vsp1_video_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct vsp1_video *video = vb2_get_drv_priv(vq); const struct v4l2_pix_format_mplane *format; struct v4l2_pix_format_mplane pix_mp; @@ -741,8 +821,9 @@ vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf); const struct v4l2_pix_format_mplane *format = &video->format; unsigned int i; @@ -762,9 +843,10 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) static void vsp1_video_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); - struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf); unsigned long flags; bool empty; @@ -875,7 +957,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) /* Remove all buffers from the IRQ queue. */ spin_lock_irqsave(&video->irqlock, flags); list_for_each_entry(buffer, &video->irqqueue, queue) - vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); } diff --git a/kernel/drivers/media/platform/vsp1/vsp1_video.h b/kernel/drivers/media/platform/vsp1/vsp1_video.h index fd2851a82..a929aa81c 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_video.h +++ b/kernel/drivers/media/platform/vsp1/vsp1_video.h @@ -1,7 +1,7 @@ /* * vsp1_video.h -- R-Car VSP1 Video Node * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -18,7 +18,7 @@ #include #include -#include +#include struct vsp1_video; @@ -94,7 +94,7 @@ static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) } struct vsp1_video_buffer { - struct vb2_buffer buf; + struct vb2_v4l2_buffer buf; struct list_head queue; dma_addr_t addr[3]; @@ -102,9 +102,9 @@ struct vsp1_video_buffer { }; static inline struct vsp1_video_buffer * -to_vsp1_video_buffer(struct vb2_buffer *vb) +to_vsp1_video_buffer(struct vb2_v4l2_buffer *vbuf) { - return container_of(vb, struct vsp1_video_buffer, buf); + return container_of(vbuf, struct vsp1_video_buffer, buf); } struct vsp1_video_operations { @@ -149,4 +149,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_entity *input, unsigned int alpha); +void vsp1_pipelines_suspend(struct vsp1_device *vsp1); +void vsp1_pipelines_resume(struct vsp1_device *vsp1); + #endif /* __VSP1_VIDEO_H__ */ diff --git a/kernel/drivers/media/platform/vsp1/vsp1_wpf.c b/kernel/drivers/media/platform/vsp1/vsp1_wpf.c index 1d2b3a2f1..95b62f4f7 100644 --- a/kernel/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/kernel/drivers/media/platform/vsp1/vsp1_wpf.c @@ -201,9 +201,9 @@ static void wpf_vdev_queue(struct vsp1_video *video, struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video); vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]); - if (buf->buf.num_planes > 1) + if (buf->buf.vb2_buf.num_planes > 1) vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]); - if (buf->buf.num_planes > 2) + if (buf->buf.vb2_buf.num_planes > 2) vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]); } diff --git a/kernel/drivers/media/platform/xilinx/Kconfig b/kernel/drivers/media/platform/xilinx/Kconfig index d7324c726..84bae795b 100644 --- a/kernel/drivers/media/platform/xilinx/Kconfig +++ b/kernel/drivers/media/platform/xilinx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_XILINX tristate "Xilinx Video IP (EXPERIMENTAL)" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- Driver for Xilinx Video IP Pipelines diff --git a/kernel/drivers/media/platform/xilinx/xilinx-dma.c b/kernel/drivers/media/platform/xilinx/xilinx-dma.c index efde88adf..d11cc7072 100644 --- a/kernel/drivers/media/platform/xilinx/xilinx-dma.c +++ b/kernel/drivers/media/platform/xilinx/xilinx-dma.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "xilinx-dma.h" @@ -285,7 +285,7 @@ done: * @dma: DMA channel that uses the buffer */ struct xvip_dma_buffer { - struct vb2_buffer buf; + struct vb2_v4l2_buffer buf; struct list_head queue; struct xvip_dma *dma; }; @@ -301,18 +301,19 @@ static void xvip_dma_complete(void *param) list_del(&buf->queue); spin_unlock(&dma->queued_lock); - buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; - buf->buf.v4l2_buf.sequence = dma->sequence++; - v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp); - vb2_set_plane_payload(&buf->buf, 0, dma->format.sizeimage); - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = dma->sequence++; + v4l2_get_timestamp(&buf->buf.timestamp); + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, dma->format.sizeimage); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); } static int -xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +xvip_dma_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct xvip_dma *dma = vb2_get_drv_priv(vq); /* Make sure the image size is large enough. */ @@ -329,8 +330,9 @@ xvip_dma_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int xvip_dma_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); - struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); buf->dma = dma; @@ -339,8 +341,9 @@ static int xvip_dma_buffer_prepare(struct vb2_buffer *vb) static void xvip_dma_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); - struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vb); + struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); struct dma_async_tx_descriptor *desc; dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); u32 flags; @@ -367,7 +370,7 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); if (!desc) { dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n"); - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); return; } desc->callback = xvip_dma_complete; @@ -434,7 +437,7 @@ error: /* Give back all queued buffers to videobuf2. */ spin_lock_irq(&dma->queued_lock); list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_QUEUED); list_del(&buf->queue); } spin_unlock_irq(&dma->queued_lock); @@ -461,7 +464,7 @@ static void xvip_dma_stop_streaming(struct vb2_queue *vq) /* Give back all queued buffers to videobuf2. */ spin_lock_irq(&dma->queued_lock); list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); list_del(&buf->queue); } spin_unlock_irq(&dma->queued_lock); @@ -653,7 +656,7 @@ static const struct v4l2_file_operations xvip_dma_fops = { int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, enum v4l2_buf_type type, unsigned int port) { - char name[14]; + char name[16]; int ret; dma->xdev = xdev; @@ -699,8 +702,10 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, /* ... and the buffers queue... */ dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev); - if (IS_ERR(dma->alloc_ctx)) + if (IS_ERR(dma->alloc_ctx)) { + ret = PTR_ERR(dma->alloc_ctx); goto error; + } /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() * V4L2 APIs would be inefficient. Testing on the command line with a @@ -725,7 +730,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, } /* ... and the DMA channel. */ - sprintf(name, "port%u", port); + snprintf(name, sizeof(name), "port%u", port); dma->dma = dma_request_slave_channel(dma->xdev->dev, name); if (dma->dma == NULL) { dev_err(dma->xdev->dev, "no VDMA channel found\n"); diff --git a/kernel/drivers/media/platform/xilinx/xilinx-dma.h b/kernel/drivers/media/platform/xilinx/xilinx-dma.h index a540111f8..7a1621a2e 100644 --- a/kernel/drivers/media/platform/xilinx/xilinx-dma.h +++ b/kernel/drivers/media/platform/xilinx/xilinx-dma.h @@ -22,7 +22,7 @@ #include #include -#include +#include struct dma_chan; struct xvip_composite_device; diff --git a/kernel/drivers/media/radio/radio-si476x.c b/kernel/drivers/media/radio/radio-si476x.c index dccf58691..9cbb8cdf0 100644 --- a/kernel/drivers/media/radio/radio-si476x.c +++ b/kernel/drivers/media/radio/radio-si476x.c @@ -568,8 +568,8 @@ static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio, err = regcache_sync_region(radio->core->regmap, SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE, SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT); - if (err < 0) - return err; + if (err < 0) + return err; err = regcache_sync_region(radio->core->regmap, SI476X_PROP_AUDIO_DEEMPHASIS, diff --git a/kernel/drivers/media/radio/radio-tea5764.c b/kernel/drivers/media/radio/radio-tea5764.c index cc3990111..a1930b300 100644 --- a/kernel/drivers/media/radio/radio-tea5764.c +++ b/kernel/drivers/media/radio/radio-tea5764.c @@ -526,7 +526,6 @@ MODULE_DEVICE_TABLE(i2c, tea5764_id); static struct i2c_driver tea5764_i2c_driver = { .driver = { .name = "radio-tea5764", - .owner = THIS_MODULE, }, .probe = tea5764_i2c_probe, .remove = tea5764_i2c_remove, diff --git a/kernel/drivers/media/radio/radio-timb.c b/kernel/drivers/media/radio/radio-timb.c index e6b55edc8..04baafe5e 100644 --- a/kernel/drivers/media/radio/radio-timb.c +++ b/kernel/drivers/media/radio/radio-timb.c @@ -138,8 +138,10 @@ static int timbradio_probe(struct platform_device *pdev) i2c_get_adapter(pdata->i2c_adapter), pdata->tuner, NULL); tr->sd_dsp = v4l2_i2c_new_subdev_board(&tr->v4l2_dev, i2c_get_adapter(pdata->i2c_adapter), pdata->dsp, NULL); - if (tr->sd_tuner == NULL || tr->sd_dsp == NULL) + if (tr->sd_tuner == NULL || tr->sd_dsp == NULL) { + err = -ENODEV; goto err_video_req; + } tr->v4l2_dev.ctrl_handler = tr->sd_dsp->ctrl_handler; diff --git a/kernel/drivers/media/radio/saa7706h.c b/kernel/drivers/media/radio/saa7706h.c index ec805b09c..ba8e357ba 100644 --- a/kernel/drivers/media/radio/saa7706h.c +++ b/kernel/drivers/media/radio/saa7706h.c @@ -336,19 +336,7 @@ static const struct v4l2_ctrl_ops saa7706h_ctrl_ops = { .s_ctrl = saa7706h_s_ctrl, }; -static const struct v4l2_subdev_core_ops saa7706h_core_ops = { - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, -}; - -static const struct v4l2_subdev_ops saa7706h_ops = { - .core = &saa7706h_core_ops, -}; +static const struct v4l2_subdev_ops empty_ops = {}; /* * Generic i2c probe @@ -373,7 +361,7 @@ static int saa7706h_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa7706h_ops); + v4l2_i2c_subdev_init(sd, client, &empty_ops); v4l2_ctrl_handler_init(&state->hdl, 4); v4l2_ctrl_new_std(&state->hdl, &saa7706h_ctrl_ops, @@ -429,7 +417,6 @@ MODULE_DEVICE_TABLE(i2c, saa7706h_id); static struct i2c_driver saa7706h_driver = { .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, }, .probe = saa7706h_probe, diff --git a/kernel/drivers/media/radio/si470x/radio-si470x-i2c.c b/kernel/drivers/media/radio/si470x/radio-si470x-i2c.c index 2a497c80c..471d6a8ae 100644 --- a/kernel/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/kernel/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -384,14 +384,14 @@ static int si470x_i2c_probe(struct i2c_client *client, goto err_radio; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", - radio->registers[DEVICEID], radio->registers[CHIPID]); - if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + radio->registers[DEVICEID], radio->registers[SI_CHIPID]); + if ((radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE) < RADIO_FW_VERSION) { dev_warn(&client->dev, "This driver is known to work with " "firmware version %hu,\n", RADIO_FW_VERSION); dev_warn(&client->dev, "but the device has firmware version %hu.\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE); + radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE); version_warning = 1; } @@ -421,7 +421,8 @@ static int si470x_i2c_probe(struct i2c_client *client, init_waitqueue_head(&radio->read_queue); retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt, - IRQF_TRIGGER_FALLING, DRIVER_NAME, radio); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRIVER_NAME, + radio); if (retval) { dev_err(&client->dev, "Failed to register interrupt\n"); goto err_rds; diff --git a/kernel/drivers/media/radio/si470x/radio-si470x-usb.c b/kernel/drivers/media/radio/si470x/radio-si470x-usb.c index 57f0bc3b6..091d793f6 100644 --- a/kernel/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/kernel/drivers/media/radio/si470x/radio-si470x-usb.c @@ -686,14 +686,14 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, goto err_ctrl; } dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", - radio->registers[DEVICEID], radio->registers[CHIPID]); - if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { + radio->registers[DEVICEID], radio->registers[SI_CHIPID]); + if ((radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE) < RADIO_FW_VERSION) { dev_warn(&intf->dev, "This driver is known to work with " "firmware version %hu,\n", RADIO_FW_VERSION); dev_warn(&intf->dev, "but the device has firmware version %hu.\n", - radio->registers[CHIPID] & CHIPID_FIRMWARE); + radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE); version_warning = 1; } diff --git a/kernel/drivers/media/radio/si470x/radio-si470x.h b/kernel/drivers/media/radio/si470x/radio-si470x.h index 4b7660470..6c0ca9007 100644 --- a/kernel/drivers/media/radio/si470x/radio-si470x.h +++ b/kernel/drivers/media/radio/si470x/radio-si470x.h @@ -54,10 +54,10 @@ #define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ #define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ -#define CHIPID 1 /* Chip ID */ -#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ -#define CHIPID_DEV 0x0200 /* bits 09..09: Device */ -#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ +#define SI_CHIPID 1 /* Chip ID */ +#define SI_CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ +#define SI_CHIPID_DEV 0x0200 /* bits 09..09: Device */ +#define SI_CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ #define POWERCFG 2 /* Power Configuration */ #define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ diff --git a/kernel/drivers/media/radio/si4713/si4713.c b/kernel/drivers/media/radio/si4713/si4713.c index e9d03ac69..0b04b5657 100644 --- a/kernel/drivers/media/radio/si4713/si4713.c +++ b/kernel/drivers/media/radio/si4713/si4713.c @@ -1609,8 +1609,10 @@ static int si4713_probe(struct i2c_client *client, return 0; si4713_pdev = platform_device_alloc("radio-si4713", -1); - if (!si4713_pdev) + if (!si4713_pdev) { + rval = -ENOMEM; goto put_main_pdev; + } si4713_pdev_pdata.subdev = client; rval = platform_device_add_data(si4713_pdev, &si4713_pdev_pdata, diff --git a/kernel/drivers/media/radio/tef6862.c b/kernel/drivers/media/radio/tef6862.c index a9319a24c..9f879f0ec 100644 --- a/kernel/drivers/media/radio/tef6862.c +++ b/kernel/drivers/media/radio/tef6862.c @@ -195,7 +195,6 @@ MODULE_DEVICE_TABLE(i2c, tef6862_id); static struct i2c_driver tef6862_driver = { .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, }, .probe = tef6862_probe, diff --git a/kernel/drivers/media/radio/wl128x/Kconfig b/kernel/drivers/media/radio/wl128x/Kconfig index 9d6574beb..c9e349b16 100644 --- a/kernel/drivers/media/radio/wl128x/Kconfig +++ b/kernel/drivers/media/radio/wl128x/Kconfig @@ -4,8 +4,8 @@ menu "Texas Instruments WL128x FM driver (ST based)" config RADIO_WL128X tristate "Texas Instruments WL128x FM Radio" - depends on VIDEO_V4L2 && RFKILL && GPIOLIB && TTY - depends on TI_ST + depends on VIDEO_V4L2 && RFKILL && TTY && TI_ST + depends on GPIOLIB || COMPILE_TEST help Choose Y here if you have this FM radio chip. diff --git a/kernel/drivers/media/radio/wl128x/fmdrv.h b/kernel/drivers/media/radio/wl128x/fmdrv.h index a587c9bac..dd203de5d 100644 --- a/kernel/drivers/media/radio/wl128x/fmdrv.h +++ b/kernel/drivers/media/radio/wl128x/fmdrv.h @@ -210,7 +210,7 @@ struct fmdev { spinlock_t resp_skb_lock; /* To protect access to received SKB */ long flag; /* FM driver state machine info */ - u8 streg_cbdata; /* status of ST registration */ + int streg_cbdata; /* status of ST registration */ struct sk_buff_head rx_q; /* RX queue */ struct tasklet_struct rx_task; /* RX Tasklet */ diff --git a/kernel/drivers/media/radio/wl128x/fmdrv_common.c b/kernel/drivers/media/radio/wl128x/fmdrv_common.c index 704397f3c..ebc73b034 100644 --- a/kernel/drivers/media/radio/wl128x/fmdrv_common.c +++ b/kernel/drivers/media/radio/wl128x/fmdrv_common.c @@ -689,7 +689,6 @@ static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af) static void fm_rdsparse_swapbytes(struct fmdev *fmdev, struct fm_rdsdata_format *rds_format) { - u8 byte1; u8 index = 0; u8 *rds_buff; @@ -701,9 +700,7 @@ static void fm_rdsparse_swapbytes(struct fmdev *fmdev, if (fmdev->asci_id != 0x6350) { rds_buff = &rds_format->data.groupdatabuff.buff[0]; while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) { - byte1 = rds_buff[index]; - rds_buff[index] = rds_buff[index + 1]; - rds_buff[index + 1] = byte1; + swap(rds_buff[index], rds_buff[index + 1]); index += 2; } } diff --git a/kernel/drivers/media/rc/Kconfig b/kernel/drivers/media/rc/Kconfig index ddfab256b..b6e13116c 100644 --- a/kernel/drivers/media/rc/Kconfig +++ b/kernel/drivers/media/rc/Kconfig @@ -371,21 +371,21 @@ config RC_ST tristate "ST remote control receiver" depends on RC_CORE depends on ARCH_STI || COMPILE_TEST - help - Say Y here if you want support for ST remote control driver - which allows both IR and UHF RX. - The driver passes raw pulse and space information to the LIRC decoder. + ---help--- + Say Y here if you want support for ST remote control driver + which allows both IR and UHF RX. + The driver passes raw pulse and space information to the LIRC decoder. - If you're not sure, select N here. + If you're not sure, select N here. config IR_SUNXI - tristate "SUNXI IR remote control" - depends on RC_CORE - depends on ARCH_SUNXI || COMPILE_TEST - ---help--- - Say Y if you want to use sunXi internal IR Controller - - To compile this driver as a module, choose M here: the module will - be called sunxi-ir. + tristate "SUNXI IR remote control" + depends on RC_CORE + depends on ARCH_SUNXI || COMPILE_TEST + ---help--- + Say Y if you want to use sunXi internal IR Controller + + To compile this driver as a module, choose M here: the module will + be called sunxi-ir. endif #RC_DEVICES diff --git a/kernel/drivers/media/rc/fintek-cir.c b/kernel/drivers/media/rc/fintek-cir.c index 5c63c2ec6..bd7b3bdb1 100644 --- a/kernel/drivers/media/rc/fintek-cir.c +++ b/kernel/drivers/media/rc/fintek-cir.c @@ -33,7 +33,6 @@ #include #include #include -#include #include "fintek-cir.h" diff --git a/kernel/drivers/media/rc/gpio-ir-recv.c b/kernel/drivers/media/rc/gpio-ir-recv.c index 229853d68..7dbc9ca6d 100644 --- a/kernel/drivers/media/rc/gpio-ir-recv.c +++ b/kernel/drivers/media/rc/gpio-ir-recv.c @@ -59,7 +59,7 @@ static int gpio_ir_recv_get_devtree_pdata(struct device *dev, return 0; } -static struct of_device_id gpio_ir_recv_of_match[] = { +static const struct of_device_id gpio_ir_recv_of_match[] = { { .compatible = "gpio-ir-receiver", }, { }, }; @@ -78,7 +78,7 @@ static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) int rc = 0; enum raw_event_type type = IR_SPACE; - gval = gpio_get_value_cansleep(gpio_dev->gpio_nr); + gval = gpio_get_value(gpio_dev->gpio_nr); if (gval < 0) goto err_get_value; diff --git a/kernel/drivers/media/rc/ir-hix5hd2.c b/kernel/drivers/media/rc/ir-hix5hd2.c index 58ec59862..d0549fba7 100644 --- a/kernel/drivers/media/rc/ir-hix5hd2.c +++ b/kernel/drivers/media/rc/ir-hix5hd2.c @@ -63,7 +63,7 @@ struct hix5hd2_ir_priv { int irq; - void volatile __iomem *base; + void __iomem *base; struct device *dev; struct rc_dev *rdev; struct regmap *regmap; @@ -213,8 +213,8 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(dev, res); - if (IS_ERR((__force void *)priv->base)) - return PTR_ERR((__force void *)priv->base); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); priv->irq = platform_get_irq(pdev, 0); if (priv->irq < 0) { @@ -257,7 +257,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) goto clkerr; if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt, - IRQF_NO_SUSPEND, pdev->name, priv) < 0) { + 0, pdev->name, priv) < 0) { dev_err(dev, "IRQ %d register failed\n", priv->irq); ret = -EINVAL; goto regerr; @@ -319,7 +319,7 @@ static int hix5hd2_ir_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend, hix5hd2_ir_resume); -static struct of_device_id hix5hd2_ir_table[] = { +static const struct of_device_id hix5hd2_ir_table[] = { { .compatible = "hisilicon,hix5hd2-ir", }, {}, }; diff --git a/kernel/drivers/media/rc/ir-lirc-codec.c b/kernel/drivers/media/rc/ir-lirc-codec.c index 98893a833..a32659fcd 100644 --- a/kernel/drivers/media/rc/ir-lirc-codec.c +++ b/kernel/drivers/media/rc/ir-lirc-codec.c @@ -35,9 +35,6 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!(dev->enabled_protocols & RC_BIT_LIRC)) - return 0; - if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) return -EINVAL; @@ -424,7 +421,7 @@ static int ir_lirc_unregister(struct rc_dev *dev) } static struct ir_raw_handler lirc_handler = { - .protocols = RC_BIT_LIRC, + .protocols = 0, .decode = ir_lirc_decode, .raw_register = ir_lirc_register, .raw_unregister = ir_lirc_unregister, diff --git a/kernel/drivers/media/rc/ir-sony-decoder.c b/kernel/drivers/media/rc/ir-sony-decoder.c index d12dc3da5..58ef06f35 100644 --- a/kernel/drivers/media/rc/ir-sony-decoder.c +++ b/kernel/drivers/media/rc/ir-sony-decoder.c @@ -125,30 +125,27 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) switch (data->count) { case 12: - if (!(dev->enabled_protocols & RC_BIT_SONY12)) { - data->state = STATE_INACTIVE; - return 0; - } + if (!(dev->enabled_protocols & RC_BIT_SONY12)) + goto finish_state_machine; + device = bitrev8((data->bits << 3) & 0xF8); subdevice = 0; function = bitrev8((data->bits >> 4) & 0xFE); protocol = RC_TYPE_SONY12; break; case 15: - if (!(dev->enabled_protocols & RC_BIT_SONY15)) { - data->state = STATE_INACTIVE; - return 0; - } + if (!(dev->enabled_protocols & RC_BIT_SONY15)) + goto finish_state_machine; + device = bitrev8((data->bits >> 0) & 0xFF); subdevice = 0; function = bitrev8((data->bits >> 7) & 0xFE); protocol = RC_TYPE_SONY15; break; case 20: - if (!(dev->enabled_protocols & RC_BIT_SONY20)) { - data->state = STATE_INACTIVE; - return 0; - } + if (!(dev->enabled_protocols & RC_BIT_SONY20)) + goto finish_state_machine; + device = bitrev8((data->bits >> 5) & 0xF8); subdevice = bitrev8((data->bits >> 0) & 0xFF); function = bitrev8((data->bits >> 12) & 0xFE); @@ -162,8 +159,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = device << 16 | subdevice << 8 | function; IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode); rc_keydown(dev, protocol, scancode, 0); - data->state = STATE_INACTIVE; - return 0; + goto finish_state_machine; } out: @@ -171,6 +167,10 @@ out: data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; + +finish_state_machine: + data->state = STATE_INACTIVE; + return 0; } static struct ir_raw_handler sony_handler = { diff --git a/kernel/drivers/media/rc/keymaps/Makefile b/kernel/drivers/media/rc/keymaps/Makefile index abf607942..fbbd3bbcd 100644 --- a/kernel/drivers/media/rc/keymaps/Makefile +++ b/kernel/drivers/media/rc/keymaps/Makefile @@ -84,7 +84,10 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ rc-tbs-nec.o \ + rc-technisat-ts35.o \ rc-technisat-usb2.o \ + rc-terratec-cinergy-c-pci.o \ + rc-terratec-cinergy-s2-hd.o \ rc-terratec-cinergy-xs.o \ rc-terratec-slim.o \ rc-terratec-slim-2.o \ @@ -94,6 +97,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ + rc-twinhan-dtv-cab-ci.o \ rc-twinhan1027.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ diff --git a/kernel/drivers/media/rc/keymaps/rc-lirc.c b/kernel/drivers/media/rc/keymaps/rc-lirc.c index fbf08fa6f..e172f5db5 100644 --- a/kernel/drivers/media/rc/keymaps/rc-lirc.c +++ b/kernel/drivers/media/rc/keymaps/rc-lirc.c @@ -20,7 +20,7 @@ static struct rc_map_list lirc_map = { .map = { .scan = lirc, .size = ARRAY_SIZE(lirc), - .rc_type = RC_TYPE_LIRC, + .rc_type = RC_TYPE_OTHER, .name = RC_MAP_LIRC, } }; diff --git a/kernel/drivers/media/rc/keymaps/rc-lme2510.c b/kernel/drivers/media/rc/keymaps/rc-lme2510.c index 51f18bb50..2b0027c41 100644 --- a/kernel/drivers/media/rc/keymaps/rc-lme2510.c +++ b/kernel/drivers/media/rc/keymaps/rc-lme2510.c @@ -15,74 +15,74 @@ static struct rc_map_table lme2510_rc[] = { /* Type 1 - 26 buttons */ - { 0x10ed45, KEY_0 }, - { 0x10ed5f, KEY_1 }, - { 0x10ed50, KEY_2 }, - { 0x10ed5d, KEY_3 }, - { 0x10ed41, KEY_4 }, - { 0x10ed0a, KEY_5 }, - { 0x10ed42, KEY_6 }, - { 0x10ed47, KEY_7 }, - { 0x10ed49, KEY_8 }, - { 0x10ed05, KEY_9 }, - { 0x10ed43, KEY_POWER }, - { 0x10ed46, KEY_SUBTITLE }, - { 0x10ed06, KEY_PAUSE }, - { 0x10ed03, KEY_MEDIA_REPEAT}, - { 0x10ed02, KEY_PAUSE }, - { 0x10ed5e, KEY_VOLUMEUP }, - { 0x10ed5c, KEY_VOLUMEDOWN }, - { 0x10ed09, KEY_CHANNELUP }, - { 0x10ed1a, KEY_CHANNELDOWN }, - { 0x10ed1e, KEY_PLAY }, - { 0x10ed1b, KEY_ZOOM }, - { 0x10ed59, KEY_MUTE }, - { 0x10ed5a, KEY_TV }, - { 0x10ed18, KEY_RECORD }, - { 0x10ed07, KEY_EPG }, - { 0x10ed01, KEY_STOP }, + { 0xef12ba45, KEY_0 }, + { 0xef12a05f, KEY_1 }, + { 0xef12af50, KEY_2 }, + { 0xef12a25d, KEY_3 }, + { 0xef12be41, KEY_4 }, + { 0xef12f50a, KEY_5 }, + { 0xef12bd42, KEY_6 }, + { 0xef12b847, KEY_7 }, + { 0xef12b649, KEY_8 }, + { 0xef12fa05, KEY_9 }, + { 0xef12bc43, KEY_POWER }, + { 0xef12b946, KEY_SUBTITLE }, + { 0xef12f906, KEY_PAUSE }, + { 0xef12fc03, KEY_MEDIA_REPEAT}, + { 0xef12fd02, KEY_PAUSE }, + { 0xef12a15e, KEY_VOLUMEUP }, + { 0xef12a35c, KEY_VOLUMEDOWN }, + { 0xef12f609, KEY_CHANNELUP }, + { 0xef12e51a, KEY_CHANNELDOWN }, + { 0xef12e11e, KEY_PLAY }, + { 0xef12e41b, KEY_ZOOM }, + { 0xef12a659, KEY_MUTE }, + { 0xef12a55a, KEY_TV }, + { 0xef12e718, KEY_RECORD }, + { 0xef12f807, KEY_EPG }, + { 0xef12fe01, KEY_STOP }, /* Type 2 - 20 buttons */ - { 0xbf15, KEY_0 }, - { 0xbf08, KEY_1 }, - { 0xbf09, KEY_2 }, - { 0xbf0a, KEY_3 }, - { 0xbf0c, KEY_4 }, - { 0xbf0d, KEY_5 }, - { 0xbf0e, KEY_6 }, - { 0xbf10, KEY_7 }, - { 0xbf11, KEY_8 }, - { 0xbf12, KEY_9 }, - { 0xbf00, KEY_POWER }, - { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xbf1a, KEY_PAUSE }, /* Timeshift */ - { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xbf01, KEY_CHANNELUP }, - { 0xbf05, KEY_CHANNELDOWN }, - { 0xbf14, KEY_ZOOM }, - { 0xbf18, KEY_RECORD }, - { 0xbf16, KEY_STOP }, + { 0xff40ea15, KEY_0 }, + { 0xff40f708, KEY_1 }, + { 0xff40f609, KEY_2 }, + { 0xff40f50a, KEY_3 }, + { 0xff40f30c, KEY_4 }, + { 0xff40f20d, KEY_5 }, + { 0xff40f10e, KEY_6 }, + { 0xff40ef10, KEY_7 }, + { 0xff40ee11, KEY_8 }, + { 0xff40ed12, KEY_9 }, + { 0xff40ff00, KEY_POWER }, + { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ + { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xff40fe01, KEY_CHANNELUP }, + { 0xff40fa05, KEY_CHANNELDOWN }, + { 0xff40eb14, KEY_ZOOM }, + { 0xff40e718, KEY_RECORD }, + { 0xff40e916, KEY_STOP }, /* Type 3 - 20 buttons */ - { 0x1c, KEY_0 }, - { 0x07, KEY_1 }, - { 0x15, KEY_2 }, - { 0x09, KEY_3 }, - { 0x16, KEY_4 }, - { 0x19, KEY_5 }, - { 0x0d, KEY_6 }, - { 0x0c, KEY_7 }, - { 0x18, KEY_8 }, - { 0x5e, KEY_9 }, - { 0x45, KEY_POWER }, - { 0x44, KEY_MEDIA_REPEAT}, /* Recall */ - { 0x4a, KEY_PAUSE }, /* Timeshift */ - { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0x46, KEY_CHANNELUP }, - { 0x40, KEY_CHANNELDOWN }, - { 0x08, KEY_ZOOM }, - { 0x42, KEY_RECORD }, - { 0x5a, KEY_STOP }, + { 0xff00e31c, KEY_0 }, + { 0xff00f807, KEY_1 }, + { 0xff00ea15, KEY_2 }, + { 0xff00f609, KEY_3 }, + { 0xff00e916, KEY_4 }, + { 0xff00e619, KEY_5 }, + { 0xff00f20d, KEY_6 }, + { 0xff00f30c, KEY_7 }, + { 0xff00e718, KEY_8 }, + { 0xff00a15e, KEY_9 }, + { 0xff00ba45, KEY_POWER }, + { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ + { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xff00b946, KEY_CHANNELUP }, + { 0xff00bf40, KEY_CHANNELDOWN }, + { 0xff00f708, KEY_ZOOM }, + { 0xff00bd42, KEY_RECORD }, + { 0xff00a55a, KEY_STOP }, }; static struct rc_map_list lme2510_map = { diff --git a/kernel/drivers/media/rc/keymaps/rc-technisat-ts35.c b/kernel/drivers/media/rc/keymaps/rc-technisat-ts35.c new file mode 100644 index 000000000..3328cbefa --- /dev/null +++ b/kernel/drivers/media/rc/keymaps/rc-technisat-ts35.c @@ -0,0 +1,76 @@ +/* rc-technisat-ts35.c - Keytable for TechniSat TS35 remote + * + * Copyright (c) 2013 by Jan Klötzke + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table technisat_ts35[] = { + {0x32, KEY_MUTE}, + {0x07, KEY_MEDIA}, + {0x1c, KEY_AB}, + {0x33, KEY_POWER}, + + {0x3e, KEY_1}, + {0x3d, KEY_2}, + {0x3c, KEY_3}, + {0x3b, KEY_4}, + {0x3a, KEY_5}, + {0x39, KEY_6}, + {0x38, KEY_7}, + {0x37, KEY_8}, + {0x36, KEY_9}, + {0x3f, KEY_0}, + {0x35, KEY_DIGITS}, + {0x2c, KEY_TV}, + + {0x20, KEY_INFO}, + {0x2d, KEY_MENU}, + {0x1f, KEY_UP}, + {0x1e, KEY_DOWN}, + {0x2e, KEY_LEFT}, + {0x2f, KEY_RIGHT}, + {0x28, KEY_OK}, + {0x10, KEY_EPG}, + {0x1d, KEY_BACK}, + + {0x14, KEY_RED}, + {0x13, KEY_GREEN}, + {0x12, KEY_YELLOW}, + {0x11, KEY_BLUE}, + + {0x09, KEY_SELECT}, + {0x03, KEY_TEXT}, + {0x16, KEY_STOP}, + {0x30, KEY_HELP}, +}; + +static struct rc_map_list technisat_ts35_map = { + .map = { + .scan = technisat_ts35, + .size = ARRAY_SIZE(technisat_ts35), + .rc_type = RC_TYPE_UNKNOWN, + .name = RC_MAP_TECHNISAT_TS35, + } +}; + +static int __init init_rc_map(void) +{ + return rc_map_register(&technisat_ts35_map); +} + +static void __exit exit_rc_map(void) +{ + rc_map_unregister(&technisat_ts35_map); +} + +module_init(init_rc_map) +module_exit(exit_rc_map) + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c b/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c new file mode 100644 index 000000000..7958f4585 --- /dev/null +++ b/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c @@ -0,0 +1,88 @@ +/* keytable for Terratec Cinergy C PCI Remote Controller + * + * Copyright (c) 2010 by Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table terratec_cinergy_c_pci[] = { + { 0x3e, KEY_POWER}, + { 0x3d, KEY_1}, + { 0x3c, KEY_2}, + { 0x3b, KEY_3}, + { 0x3a, KEY_4}, + { 0x39, KEY_5}, + { 0x38, KEY_6}, + { 0x37, KEY_7}, + { 0x36, KEY_8}, + { 0x35, KEY_9}, + { 0x34, KEY_VIDEO_NEXT}, /* AV */ + { 0x33, KEY_0}, + { 0x32, KEY_REFRESH}, + { 0x30, KEY_EPG}, + { 0x2f, KEY_UP}, + { 0x2e, KEY_LEFT}, + { 0x2d, KEY_OK}, + { 0x2c, KEY_RIGHT}, + { 0x2b, KEY_DOWN}, + { 0x29, KEY_INFO}, + { 0x28, KEY_RED}, + { 0x27, KEY_GREEN}, + { 0x26, KEY_YELLOW}, + { 0x25, KEY_BLUE}, + { 0x24, KEY_CHANNELUP}, + { 0x23, KEY_VOLUMEUP}, + { 0x22, KEY_MUTE}, + { 0x21, KEY_VOLUMEDOWN}, + { 0x20, KEY_CHANNELDOWN}, + { 0x1f, KEY_PAUSE}, + { 0x1e, KEY_HOME}, + { 0x1d, KEY_MENU}, /* DVD Menu */ + { 0x1c, KEY_SUBTITLE}, + { 0x1b, KEY_TEXT}, /* Teletext */ + { 0x1a, KEY_DELETE}, + { 0x19, KEY_TV}, + { 0x18, KEY_DVD}, + { 0x17, KEY_STOP}, + { 0x16, KEY_VIDEO}, + { 0x15, KEY_AUDIO}, /* Music */ + { 0x14, KEY_SCREEN}, /* Pic */ + { 0x13, KEY_PLAY}, + { 0x12, KEY_BACK}, + { 0x11, KEY_REWIND}, + { 0x10, KEY_FASTFORWARD}, + { 0x0b, KEY_PREVIOUS}, + { 0x07, KEY_RECORD}, + { 0x03, KEY_NEXT}, + +}; + +static struct rc_map_list terratec_cinergy_c_pci_map = { + .map = { + .scan = terratec_cinergy_c_pci, + .size = ARRAY_SIZE(terratec_cinergy_c_pci), + .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TERRATEC_CINERGY_C_PCI, + } +}; + +static int __init init_rc_map_terratec_cinergy_c_pci(void) +{ + return rc_map_register(&terratec_cinergy_c_pci_map); +} + +static void __exit exit_rc_map_terratec_cinergy_c_pci(void) +{ + rc_map_unregister(&terratec_cinergy_c_pci_map); +} + +module_init(init_rc_map_terratec_cinergy_c_pci); +module_exit(exit_rc_map_terratec_cinergy_c_pci); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c b/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c new file mode 100644 index 000000000..1e096bbda --- /dev/null +++ b/kernel/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c @@ -0,0 +1,86 @@ +/* keytable for Terratec Cinergy S2 HD Remote Controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table terratec_cinergy_s2_hd[] = { + { 0x03, KEY_NEXT}, /* >| */ + { 0x07, KEY_RECORD}, + { 0x0b, KEY_PREVIOUS}, /* |< */ + { 0x10, KEY_FASTFORWARD}, /* >> */ + { 0x11, KEY_REWIND}, /* << */ + { 0x12, KEY_ESC}, /* Back */ + { 0x13, KEY_PLAY}, + { 0x14, KEY_IMAGES}, + { 0x15, KEY_AUDIO}, + { 0x16, KEY_MEDIA}, /* Video-Menu */ + { 0x17, KEY_STOP}, + { 0x18, KEY_DVD}, + { 0x19, KEY_TV}, + { 0x1a, KEY_DELETE}, + { 0x1b, KEY_TEXT}, + { 0x1c, KEY_SUBTITLE}, + { 0x1d, KEY_MENU}, /* DVD-Menu */ + { 0x1e, KEY_HOME}, + { 0x1f, KEY_PAUSE}, + { 0x20, KEY_CHANNELDOWN}, + { 0x21, KEY_VOLUMEDOWN}, + { 0x22, KEY_MUTE}, + { 0x23, KEY_VOLUMEUP}, + { 0x24, KEY_CHANNELUP}, + { 0x25, KEY_BLUE}, + { 0x26, KEY_YELLOW}, + { 0x27, KEY_GREEN}, + { 0x28, KEY_RED}, + { 0x29, KEY_INFO}, + { 0x2b, KEY_DOWN}, + { 0x2c, KEY_RIGHT}, + { 0x2d, KEY_OK}, + { 0x2e, KEY_LEFT}, + { 0x2f, KEY_UP}, + { 0x30, KEY_EPG}, + { 0x32, KEY_VIDEO}, /* A<=>B */ + { 0x33, KEY_0}, + { 0x34, KEY_VCR}, /* AV */ + { 0x35, KEY_9}, + { 0x36, KEY_8}, + { 0x37, KEY_7}, + { 0x38, KEY_6}, + { 0x39, KEY_5}, + { 0x3a, KEY_4}, + { 0x3b, KEY_3}, + { 0x3c, KEY_2}, + { 0x3d, KEY_1}, + { 0x3e, KEY_POWER}, + +}; + +static struct rc_map_list terratec_cinergy_s2_hd_map = { + .map = { + .scan = terratec_cinergy_s2_hd, + .size = ARRAY_SIZE(terratec_cinergy_s2_hd), + .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TERRATEC_CINERGY_S2_HD, + } +}; + +static int __init init_rc_map_terratec_cinergy_s2_hd(void) +{ + return rc_map_register(&terratec_cinergy_s2_hd_map); +} + +static void __exit exit_rc_map_terratec_cinergy_s2_hd(void) +{ + rc_map_unregister(&terratec_cinergy_s2_hd_map); +} + +module_init(init_rc_map_terratec_cinergy_s2_hd); +module_exit(exit_rc_map_terratec_cinergy_s2_hd); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c b/kernel/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c new file mode 100644 index 000000000..202500cb3 --- /dev/null +++ b/kernel/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c @@ -0,0 +1,98 @@ +/* keytable for Twinhan DTV CAB CI Remote Controller + * + * Copyright (c) 2010 by Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table twinhan_dtv_cab_ci[] = { + { 0x29, KEY_POWER}, + { 0x28, KEY_FAVORITES}, + { 0x30, KEY_TEXT}, + { 0x17, KEY_INFO}, /* Preview */ + { 0x23, KEY_EPG}, + { 0x3b, KEY_F22}, /* Record List */ + + { 0x3c, KEY_1}, + { 0x3e, KEY_2}, + { 0x39, KEY_3}, + { 0x36, KEY_4}, + { 0x22, KEY_5}, + { 0x20, KEY_6}, + { 0x32, KEY_7}, + { 0x26, KEY_8}, + { 0x24, KEY_9}, + { 0x2a, KEY_0}, + + { 0x33, KEY_CANCEL}, + { 0x2c, KEY_BACK}, + { 0x15, KEY_CLEAR}, + { 0x3f, KEY_TAB}, + { 0x10, KEY_ENTER}, + { 0x14, KEY_UP}, + { 0x0d, KEY_RIGHT}, + { 0x0e, KEY_DOWN}, + { 0x11, KEY_LEFT}, + + { 0x21, KEY_VOLUMEUP}, + { 0x35, KEY_VOLUMEDOWN}, + { 0x3d, KEY_CHANNELDOWN}, + { 0x3a, KEY_CHANNELUP}, + { 0x2e, KEY_RECORD}, + { 0x2b, KEY_PLAY}, + { 0x13, KEY_PAUSE}, + { 0x25, KEY_STOP}, + + { 0x1f, KEY_REWIND}, + { 0x2d, KEY_FASTFORWARD}, + { 0x1e, KEY_PREVIOUS}, /* Replay |< */ + { 0x1d, KEY_NEXT}, /* Skip >| */ + + { 0x0b, KEY_CAMERA}, /* Capture */ + { 0x0f, KEY_LANGUAGE}, /* SAP */ + { 0x18, KEY_MODE}, /* PIP */ + { 0x12, KEY_ZOOM}, /* Full screen */ + { 0x1c, KEY_SUBTITLE}, + { 0x2f, KEY_MUTE}, + { 0x16, KEY_F20}, /* L/R */ + { 0x38, KEY_F21}, /* Hibernate */ + + { 0x37, KEY_SWITCHVIDEOMODE}, /* A/V */ + { 0x31, KEY_AGAIN}, /* Recall */ + { 0x1a, KEY_KPPLUS}, /* Zoom+ */ + { 0x19, KEY_KPMINUS}, /* Zoom- */ + { 0x27, KEY_RED}, + { 0x0C, KEY_GREEN}, + { 0x01, KEY_YELLOW}, + { 0x00, KEY_BLUE}, +}; + +static struct rc_map_list twinhan_dtv_cab_ci_map = { + .map = { + .scan = twinhan_dtv_cab_ci, + .size = ARRAY_SIZE(twinhan_dtv_cab_ci), + .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TWINHAN_DTV_CAB_CI, + } +}; + +static int __init init_rc_map_twinhan_dtv_cab_ci(void) +{ + return rc_map_register(&twinhan_dtv_cab_ci_map); +} + +static void __exit exit_rc_map_twinhan_dtv_cab_ci(void) +{ + rc_map_unregister(&twinhan_dtv_cab_ci_map); +} + +module_init(init_rc_map_twinhan_dtv_cab_ci); +module_exit(exit_rc_map_twinhan_dtv_cab_ci); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/rc/rc-ir-raw.c b/kernel/drivers/media/rc/rc-ir-raw.c index b732ac6a2..ad260520a 100644 --- a/kernel/drivers/media/rc/rc-ir-raw.c +++ b/kernel/drivers/media/rc/rc-ir-raw.c @@ -271,7 +271,7 @@ int ir_raw_event_register(struct rc_dev *dev) spin_lock_init(&dev->raw->lock); dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw, - "rc%ld", dev->devno); + "rc%u", dev->minor); if (IS_ERR(dev->raw->thread)) { rc = PTR_ERR(dev->raw->thread); diff --git a/kernel/drivers/media/rc/rc-main.c b/kernel/drivers/media/rc/rc-main.c index 0aba9ff92..3f0f71ada 100644 --- a/kernel/drivers/media/rc/rc-main.c +++ b/kernel/drivers/media/rc/rc-main.c @@ -18,17 +18,15 @@ #include #include #include +#include #include #include #include "rc-core-priv.h" -/* Bitmap to store allocated device numbers from 0 to IRRCV_NUM_DEVICES - 1 */ -#define IRRCV_NUM_DEVICES 256 -static DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES); - /* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */ #define IR_TAB_MIN_SIZE 256 #define IR_TAB_MAX_SIZE 8192 +#define RC_DEV_MAX 256 /* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */ #define IR_KEYPRESS_TIMEOUT 250 @@ -38,6 +36,9 @@ static LIST_HEAD(rc_map_list); static DEFINE_SPINLOCK(rc_map_lock); static struct led_trigger *led_feedback; +/* Used to keep track of rc devices */ +static DEFINE_IDA(rc_ida); + static struct rc_map_list *seek_rc_map(const char *name) { struct rc_map_list *map = NULL; @@ -746,7 +747,7 @@ void rc_close(struct rc_dev *rdev) if (rdev) { mutex_lock(&rdev->lock); - if (!--rdev->users && rdev->close != NULL) + if (!--rdev->users && rdev->close != NULL) rdev->close(rdev); mutex_unlock(&rdev->lock); @@ -799,7 +800,6 @@ static struct { { RC_BIT_SANYO, "sanyo" }, { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, - { RC_BIT_LIRC, "lirc" }, { RC_BIT_XMP, "xmp" }, }; @@ -828,6 +828,23 @@ struct rc_filter_attribute { .mask = (_mask), \ } +static bool lirc_is_present(void) +{ +#if defined(CONFIG_LIRC_MODULE) + struct module *lirc; + + mutex_lock(&module_mutex); + lirc = find_module("lirc_dev"); + mutex_unlock(&module_mutex); + + return lirc ? true : false; +#elif defined(CONFIG_LIRC) + return true; +#else + return false; +#endif +} + /** * show_protocols() - shows the current/wakeup IR protocol(s) * @device: the device descriptor @@ -882,6 +899,9 @@ static ssize_t show_protocols(struct device *device, allowed &= ~proto_names[i].type; } + if (dev->driver_type == RC_DRIVER_IR_RAW && lirc_is_present()) + tmp += sprintf(tmp, "[lirc] "); + if (tmp != buf) tmp--; *tmp = '\n'; @@ -933,8 +953,12 @@ static int parse_protocol_change(u64 *protocols, const char *buf) } if (i == ARRAY_SIZE(proto_names)) { - IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + if (!strcasecmp(tmp, "lirc")) + mask = 0; + else { + IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); + return -EINVAL; + } } count++; @@ -1309,7 +1333,9 @@ int rc_register_device(struct rc_dev *dev) static bool raw_init = false; /* raw decoders loaded? */ struct rc_map *rc_map; const char *path; - int rc, devno, attr = 0; + int attr = 0; + int minor; + int rc; if (!dev || !dev->map_name) return -EINVAL; @@ -1329,13 +1355,13 @@ int rc_register_device(struct rc_dev *dev) if (dev->close) dev->input_dev->close = ir_close; - do { - devno = find_first_zero_bit(ir_core_dev_number, - IRRCV_NUM_DEVICES); - /* No free device slots */ - if (devno >= IRRCV_NUM_DEVICES) - return -ENOMEM; - } while (test_and_set_bit(devno, ir_core_dev_number)); + minor = ida_simple_get(&rc_ida, 0, RC_DEV_MAX, GFP_KERNEL); + if (minor < 0) + return minor; + + dev->minor = minor; + dev_set_name(&dev->dev, "rc%u", dev->minor); + dev_set_drvdata(&dev->dev, dev); dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; @@ -1355,9 +1381,6 @@ int rc_register_device(struct rc_dev *dev) */ mutex_lock(&dev->lock); - dev->devno = devno; - dev_set_name(&dev->dev, "rc%ld", dev->devno); - dev_set_drvdata(&dev->dev, dev); rc = device_add(&dev->dev); if (rc) goto out_unlock; @@ -1420,8 +1443,6 @@ int rc_register_device(struct rc_dev *dev) if (dev->change_protocol) { u64 rc_type = (1ll << rc_map->rc_type); - if (dev->driver_type == RC_DRIVER_IR_RAW) - rc_type |= RC_BIT_LIRC; rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; @@ -1430,8 +1451,8 @@ int rc_register_device(struct rc_dev *dev) mutex_unlock(&dev->lock); - IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n", - dev->devno, + IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", + dev->minor, dev->driver_name ? dev->driver_name : "unknown", rc_map->name ? rc_map->name : "unknown", dev->driver_type == RC_DRIVER_IR_RAW ? "raw" : "cooked"); @@ -1450,7 +1471,7 @@ out_dev: device_del(&dev->dev); out_unlock: mutex_unlock(&dev->lock); - clear_bit(dev->devno, ir_core_dev_number); + ida_simple_remove(&rc_ida, minor); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); @@ -1462,8 +1483,6 @@ void rc_unregister_device(struct rc_dev *dev) del_timer_sync(&dev->timer_keyup); - clear_bit(dev->devno, ir_core_dev_number); - if (dev->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(dev); @@ -1476,6 +1495,8 @@ void rc_unregister_device(struct rc_dev *dev) device_del(&dev->dev); + ida_simple_remove(&rc_ida, dev->minor); + rc_free_device(dev); } diff --git a/kernel/drivers/media/rc/redrat3.c b/kernel/drivers/media/rc/redrat3.c index c4def66f9..ec74244a3 100644 --- a/kernel/drivers/media/rc/redrat3.c +++ b/kernel/drivers/media/rc/redrat3.c @@ -322,7 +322,7 @@ static u32 redrat3_us_to_len(u32 microsec) u32 result; u32 divisor; - microsec &= IR_MAX_DURATION; + microsec = (microsec > IR_MAX_DURATION) ? IR_MAX_DURATION : microsec; divisor = (RR3_CLK_CONV_FACTOR / 1000); result = (u32)(microsec * divisor) / 1000; @@ -380,7 +380,8 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) if (i == 0) trailer = rawir.duration; /* cap the value to IR_MAX_DURATION */ - rawir.duration &= IR_MAX_DURATION; + rawir.duration = (rawir.duration > IR_MAX_DURATION) ? + IR_MAX_DURATION : rawir.duration; dev_dbg(dev, "storing %s with duration %d (i: %d)\n", rawir.pulse ? "pulse" : "space", rawir.duration, i); @@ -405,7 +406,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) } /* Util fn to send rr3 cmds */ -static u8 redrat3_send_cmd(int cmd, struct redrat3_dev *rr3) +static int redrat3_send_cmd(int cmd, struct redrat3_dev *rr3) { struct usb_device *udev; u8 *data; diff --git a/kernel/drivers/media/rc/st_rc.c b/kernel/drivers/media/rc/st_rc.c index 0e758ae2e..37d040158 100644 --- a/kernel/drivers/media/rc/st_rc.c +++ b/kernel/drivers/media/rc/st_rc.c @@ -22,8 +22,8 @@ struct st_rc_device { int irq; int irq_wake; struct clk *sys_clock; - volatile void __iomem *base; /* Register base address */ - volatile void __iomem *rx_base;/* RX Register base address */ + void __iomem *base; /* Register base address */ + void __iomem *rx_base;/* RX Register base address */ struct rc_dev *rdev; bool overclocking; int sample_mult; @@ -267,8 +267,8 @@ static int st_rc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rc_dev->base = devm_ioremap_resource(dev, res); - if (IS_ERR((__force void *)rc_dev->base)) { - ret = PTR_ERR((__force void *)rc_dev->base); + if (IS_ERR(rc_dev->base)) { + ret = PTR_ERR(rc_dev->base); goto err; } @@ -334,7 +334,7 @@ err: return ret; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int st_rc_suspend(struct device *dev) { struct st_rc_device *rc_dev = dev_get_drvdata(dev); @@ -381,7 +381,7 @@ static int st_rc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume); #ifdef CONFIG_OF -static struct of_device_id st_rc_match[] = { +static const struct of_device_id st_rc_match[] = { { .compatible = "st,comms-irb", }, {}, }; diff --git a/kernel/drivers/media/rc/streamzap.c b/kernel/drivers/media/rc/streamzap.c index bf4a44272..5a17cb88f 100644 --- a/kernel/drivers/media/rc/streamzap.c +++ b/kernel/drivers/media/rc/streamzap.c @@ -152,7 +152,8 @@ static void sz_push_full_pulse(struct streamzap_ir *sz, sz->signal_last.tv_usec); rawir.duration -= sz->sum; rawir.duration = US_TO_NS(rawir.duration); - rawir.duration &= IR_MAX_DURATION; + rawir.duration = (rawir.duration > IR_MAX_DURATION) ? + IR_MAX_DURATION : rawir.duration; } sz_push(sz, rawir); @@ -165,7 +166,8 @@ static void sz_push_full_pulse(struct streamzap_ir *sz, rawir.duration += SZ_RESOLUTION / 2; sz->sum += rawir.duration; rawir.duration = US_TO_NS(rawir.duration); - rawir.duration &= IR_MAX_DURATION; + rawir.duration = (rawir.duration > IR_MAX_DURATION) ? + IR_MAX_DURATION : rawir.duration; sz_push(sz, rawir); } diff --git a/kernel/drivers/media/rc/sunxi-cir.c b/kernel/drivers/media/rc/sunxi-cir.c index 7830aef3d..40f77685c 100644 --- a/kernel/drivers/media/rc/sunxi-cir.c +++ b/kernel/drivers/media/rc/sunxi-cir.c @@ -153,6 +153,8 @@ static int sunxi_ir_probe(struct platform_device *pdev) if (!ir) return -ENOMEM; + spin_lock_init(&ir->ir_lock); + if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir")) ir->fifo_size = 64; else diff --git a/kernel/drivers/media/tuners/Kconfig b/kernel/drivers/media/tuners/Kconfig index 983510d28..05998f025 100644 --- a/kernel/drivers/media/tuners/Kconfig +++ b/kernel/drivers/media/tuners/Kconfig @@ -15,7 +15,7 @@ config MEDIA_TUNER select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT menu "Customize TV tuners" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT config MEDIA_TUNER_SIMPLE @@ -220,6 +220,7 @@ config MEDIA_TUNER_E4000 config MEDIA_TUNER_FC2580 tristate "FCI FC2580 silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help FCI FC2580 silicon tuner driver. @@ -233,8 +234,9 @@ config MEDIA_TUNER_M88RS6000T Montage M88RS6000 internal tuner. config MEDIA_TUNER_TUA9001 - tristate "Infineon TUA 9001 silicon tuner" + tristate "Infineon TUA9001 silicon tuner" depends on MEDIA_SUPPORT && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help Infineon TUA 9001 silicon tuner driver. @@ -258,6 +260,7 @@ config MEDIA_TUNER_R820T tristate "Rafael Micro R820T silicon tuner" depends on MEDIA_SUPPORT && I2C default m if !MEDIA_SUBDRV_AUTOSELECT + select BITREVERSE help Rafael Micro R820T silicon tuner driver. diff --git a/kernel/drivers/media/tuners/e4000.c b/kernel/drivers/media/tuners/e4000.c index 510239f80..564a000f5 100644 --- a/kernel/drivers/media/tuners/e4000.c +++ b/kernel/drivers/media/tuners/e4000.c @@ -19,164 +19,176 @@ */ #include "e4000_priv.h" -#include -static int e4000_init(struct dvb_frontend *fe) +static int e4000_init(struct e4000_dev *dev) { - struct e4000 *s = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; - dev_dbg(&s->client->dev, "\n"); - - /* dummy I2C to ensure I2C wakes up */ - ret = regmap_write(s->regmap, 0x02, 0x40); + dev_dbg(&client->dev, "\n"); /* reset */ - ret = regmap_write(s->regmap, 0x00, 0x01); + ret = regmap_write(dev->regmap, 0x00, 0x01); if (ret) goto err; /* disable output clock */ - ret = regmap_write(s->regmap, 0x06, 0x00); + ret = regmap_write(dev->regmap, 0x06, 0x00); if (ret) goto err; - ret = regmap_write(s->regmap, 0x7a, 0x96); + ret = regmap_write(dev->regmap, 0x7a, 0x96); if (ret) goto err; /* configure gains */ - ret = regmap_bulk_write(s->regmap, 0x7e, "\x01\xfe", 2); + ret = regmap_bulk_write(dev->regmap, 0x7e, "\x01\xfe", 2); if (ret) goto err; - ret = regmap_write(s->regmap, 0x82, 0x00); + ret = regmap_write(dev->regmap, 0x82, 0x00); if (ret) goto err; - ret = regmap_write(s->regmap, 0x24, 0x05); + ret = regmap_write(dev->regmap, 0x24, 0x05); if (ret) goto err; - ret = regmap_bulk_write(s->regmap, 0x87, "\x20\x01", 2); + ret = regmap_bulk_write(dev->regmap, 0x87, "\x20\x01", 2); if (ret) goto err; - ret = regmap_bulk_write(s->regmap, 0x9f, "\x7f\x07", 2); + ret = regmap_bulk_write(dev->regmap, 0x9f, "\x7f\x07", 2); if (ret) goto err; /* DC offset control */ - ret = regmap_write(s->regmap, 0x2d, 0x1f); + ret = regmap_write(dev->regmap, 0x2d, 0x1f); if (ret) goto err; - ret = regmap_bulk_write(s->regmap, 0x70, "\x01\x01", 2); + ret = regmap_bulk_write(dev->regmap, 0x70, "\x01\x01", 2); if (ret) goto err; /* gain control */ - ret = regmap_write(s->regmap, 0x1a, 0x17); + ret = regmap_write(dev->regmap, 0x1a, 0x17); if (ret) goto err; - ret = regmap_write(s->regmap, 0x1f, 0x1a); + ret = regmap_write(dev->regmap, 0x1f, 0x1a); if (ret) goto err; - s->active = true; -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev->active = true; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int e4000_sleep(struct dvb_frontend *fe) +static int e4000_sleep(struct e4000_dev *dev) { - struct e4000 *s = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "\n"); - s->active = false; + dev->active = false; - ret = regmap_write(s->regmap, 0x00, 0x00); + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) goto err; -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int e4000_set_params(struct dvb_frontend *fe) +static int e4000_set_params(struct e4000_dev *dev) { - struct e4000 *s = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i, sigma_delta; - unsigned int pll_n, pll_f; + struct i2c_client *client = dev->client; + int ret, i; + unsigned int div_n, k, k_cw, div_out; u64 f_vco; u8 buf[5], i_data[4], q_data[4]; - dev_dbg(&s->client->dev, - "delivery_system=%d frequency=%u bandwidth_hz=%u\n", - c->delivery_system, c->frequency, c->bandwidth_hz); + if (!dev->active) { + dev_dbg(&client->dev, "tuner is sleeping\n"); + return 0; + } /* gain control manual */ - ret = regmap_write(s->regmap, 0x1a, 0x00); - if (ret) - goto err; - - /* PLL */ + ret = regmap_write(dev->regmap, 0x1a, 0x00); + if (ret) + goto err; + + /* + * Fractional-N synthesizer + * + * +----------------------------+ + * v | + * Fref +----+ +-------+ +------+ +---+ + * ------> | PD | --> | VCO | ------> | /N.F | <-- | K | + * +----+ +-------+ +------+ +---+ + * | + * | + * v + * +-------+ Fout + * | /Rout | ------> + * +-------+ + */ for (i = 0; i < ARRAY_SIZE(e4000_pll_lut); i++) { - if (c->frequency <= e4000_pll_lut[i].freq) + if (dev->f_frequency <= e4000_pll_lut[i].freq) break; } - if (i == ARRAY_SIZE(e4000_pll_lut)) { ret = -EINVAL; goto err; } - f_vco = 1ull * c->frequency * e4000_pll_lut[i].mul; - pll_n = div_u64_rem(f_vco, s->clock, &pll_f); - sigma_delta = div_u64(0x10000ULL * pll_f, s->clock); - buf[0] = pll_n; - buf[1] = (sigma_delta >> 0) & 0xff; - buf[2] = (sigma_delta >> 8) & 0xff; + #define F_REF dev->clk + div_out = e4000_pll_lut[i].div_out; + f_vco = (u64) dev->f_frequency * div_out; + /* calculate PLL integer and fractional control word */ + div_n = div_u64_rem(f_vco, F_REF, &k); + k_cw = div_u64((u64) k * 0x10000, F_REF); + + dev_dbg(&client->dev, + "frequency=%u bandwidth=%u f_vco=%llu F_REF=%u div_n=%u k=%u k_cw=%04x div_out=%u\n", + dev->f_frequency, dev->f_bandwidth, f_vco, F_REF, div_n, k, + k_cw, div_out); + + buf[0] = div_n; + buf[1] = (k_cw >> 0) & 0xff; + buf[2] = (k_cw >> 8) & 0xff; buf[3] = 0x00; - buf[4] = e4000_pll_lut[i].div; - - dev_dbg(&s->client->dev, "f_vco=%llu pll div=%d sigma_delta=%04x\n", - f_vco, buf[0], sigma_delta); - - ret = regmap_bulk_write(s->regmap, 0x09, buf, 5); + buf[4] = e4000_pll_lut[i].div_out_reg; + ret = regmap_bulk_write(dev->regmap, 0x09, buf, 5); if (ret) goto err; /* LNA filter (RF filter) */ for (i = 0; i < ARRAY_SIZE(e400_lna_filter_lut); i++) { - if (c->frequency <= e400_lna_filter_lut[i].freq) + if (dev->f_frequency <= e400_lna_filter_lut[i].freq) break; } - if (i == ARRAY_SIZE(e400_lna_filter_lut)) { ret = -EINVAL; goto err; } - ret = regmap_write(s->regmap, 0x10, e400_lna_filter_lut[i].val); + ret = regmap_write(dev->regmap, 0x10, e400_lna_filter_lut[i].val); if (ret) goto err; /* IF filters */ for (i = 0; i < ARRAY_SIZE(e4000_if_filter_lut); i++) { - if (c->bandwidth_hz <= e4000_if_filter_lut[i].freq) + if (dev->f_bandwidth <= e4000_if_filter_lut[i].freq) break; } - if (i == ARRAY_SIZE(e4000_if_filter_lut)) { ret = -EINVAL; goto err; @@ -185,48 +197,47 @@ static int e4000_set_params(struct dvb_frontend *fe) buf[0] = e4000_if_filter_lut[i].reg11_val; buf[1] = e4000_if_filter_lut[i].reg12_val; - ret = regmap_bulk_write(s->regmap, 0x11, buf, 2); + ret = regmap_bulk_write(dev->regmap, 0x11, buf, 2); if (ret) goto err; /* frequency band */ for (i = 0; i < ARRAY_SIZE(e4000_band_lut); i++) { - if (c->frequency <= e4000_band_lut[i].freq) + if (dev->f_frequency <= e4000_band_lut[i].freq) break; } - if (i == ARRAY_SIZE(e4000_band_lut)) { ret = -EINVAL; goto err; } - ret = regmap_write(s->regmap, 0x07, e4000_band_lut[i].reg07_val); + ret = regmap_write(dev->regmap, 0x07, e4000_band_lut[i].reg07_val); if (ret) goto err; - ret = regmap_write(s->regmap, 0x78, e4000_band_lut[i].reg78_val); + ret = regmap_write(dev->regmap, 0x78, e4000_band_lut[i].reg78_val); if (ret) goto err; /* DC offset */ for (i = 0; i < 4; i++) { if (i == 0) - ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7e\x24", 3); + ret = regmap_bulk_write(dev->regmap, 0x15, "\x00\x7e\x24", 3); else if (i == 1) - ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7f", 2); + ret = regmap_bulk_write(dev->regmap, 0x15, "\x00\x7f", 2); else if (i == 2) - ret = regmap_bulk_write(s->regmap, 0x15, "\x01", 1); + ret = regmap_bulk_write(dev->regmap, 0x15, "\x01", 1); else - ret = regmap_bulk_write(s->regmap, 0x16, "\x7e", 1); + ret = regmap_bulk_write(dev->regmap, 0x16, "\x7e", 1); if (ret) goto err; - ret = regmap_write(s->regmap, 0x29, 0x01); + ret = regmap_write(dev->regmap, 0x29, 0x01); if (ret) goto err; - ret = regmap_bulk_read(s->regmap, 0x2a, buf, 3); + ret = regmap_bulk_read(dev->regmap, 0x2a, buf, 3); if (ret) goto err; @@ -237,174 +248,294 @@ static int e4000_set_params(struct dvb_frontend *fe) swap(q_data[2], q_data[3]); swap(i_data[2], i_data[3]); - ret = regmap_bulk_write(s->regmap, 0x50, q_data, 4); + ret = regmap_bulk_write(dev->regmap, 0x50, q_data, 4); if (ret) goto err; - ret = regmap_bulk_write(s->regmap, 0x60, i_data, 4); + ret = regmap_bulk_write(dev->regmap, 0x60, i_data, 4); if (ret) goto err; /* gain control auto */ - ret = regmap_write(s->regmap, 0x1a, 0x17); + ret = regmap_write(dev->regmap, 0x1a, 0x17); if (ret) goto err; + + return 0; err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +/* + * V4L2 API + */ +#if IS_ENABLED(CONFIG_VIDEO_V4L2) +static const struct v4l2_frequency_band bands[] = { + { + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 59000000, + .rangehigh = 1105000000, + }, + { + .type = V4L2_TUNER_RF, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1249000000, + .rangehigh = 2208000000UL, + }, +}; + +static inline struct e4000_dev *e4000_subdev_to_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct e4000_dev, sd); +} + +static int e4000_s_power(struct v4l2_subdev *sd, int on) +{ + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "on=%d\n", on); + + if (on) + ret = e4000_init(dev); + else + ret = e4000_sleep(dev); if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + return ret; - return ret; + return e4000_set_params(dev); +} + +static const struct v4l2_subdev_core_ops e4000_subdev_core_ops = { + .s_power = e4000_s_power, +}; + +static int e4000_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "index=%d\n", v->index); + + strlcpy(v->name, "Elonics E4000", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands[0].rangelow; + v->rangehigh = bands[1].rangehigh; + return 0; } -static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +static int e4000_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) { - struct e4000 *s = fe->tuner_priv; + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "index=%d\n", v->index); + return 0; +} - *frequency = 0; /* Zero-IF */ +static int e4000_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + dev_dbg(&client->dev, "tuner=%d\n", f->tuner); + f->frequency = dev->f_frequency; return 0; } -#if IS_ENABLED(CONFIG_VIDEO_V4L2) +static int e4000_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *f) +{ + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); + + dev->f_frequency = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, bands[1].rangehigh); + return e4000_set_params(dev); +} + +static int e4000_enum_freq_bands(struct v4l2_subdev *sd, + struct v4l2_frequency_band *band) +{ + struct e4000_dev *dev = e4000_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); + + if (band->index >= ARRAY_SIZE(bands)) + return -EINVAL; + + band->capability = bands[band->index].capability; + band->rangelow = bands[band->index].rangelow; + band->rangehigh = bands[band->index].rangehigh; + return 0; +} + +static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = { + .g_tuner = e4000_g_tuner, + .s_tuner = e4000_s_tuner, + .g_frequency = e4000_g_frequency, + .s_frequency = e4000_s_frequency, + .enum_freq_bands = e4000_enum_freq_bands, +}; + +static const struct v4l2_subdev_ops e4000_subdev_ops = { + .core = &e4000_subdev_core_ops, + .tuner = &e4000_subdev_tuner_ops, +}; + static int e4000_set_lna_gain(struct dvb_frontend *fe) { - struct e4000 *s = fe->tuner_priv; + struct e4000_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; u8 u8tmp; - dev_dbg(&s->client->dev, "lna auto=%d->%d val=%d->%d\n", - s->lna_gain_auto->cur.val, s->lna_gain_auto->val, - s->lna_gain->cur.val, s->lna_gain->val); + dev_dbg(&client->dev, "lna auto=%d->%d val=%d->%d\n", + dev->lna_gain_auto->cur.val, dev->lna_gain_auto->val, + dev->lna_gain->cur.val, dev->lna_gain->val); - if (s->lna_gain_auto->val && s->if_gain_auto->cur.val) + if (dev->lna_gain_auto->val && dev->if_gain_auto->cur.val) u8tmp = 0x17; - else if (s->lna_gain_auto->val) + else if (dev->lna_gain_auto->val) u8tmp = 0x19; - else if (s->if_gain_auto->cur.val) + else if (dev->if_gain_auto->cur.val) u8tmp = 0x16; else u8tmp = 0x10; - ret = regmap_write(s->regmap, 0x1a, u8tmp); + ret = regmap_write(dev->regmap, 0x1a, u8tmp); if (ret) goto err; - if (s->lna_gain_auto->val == false) { - ret = regmap_write(s->regmap, 0x14, s->lna_gain->val); + if (dev->lna_gain_auto->val == false) { + ret = regmap_write(dev->regmap, 0x14, dev->lna_gain->val); if (ret) goto err; } -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int e4000_set_mixer_gain(struct dvb_frontend *fe) { - struct e4000 *s = fe->tuner_priv; + struct e4000_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; u8 u8tmp; - dev_dbg(&s->client->dev, "mixer auto=%d->%d val=%d->%d\n", - s->mixer_gain_auto->cur.val, s->mixer_gain_auto->val, - s->mixer_gain->cur.val, s->mixer_gain->val); + dev_dbg(&client->dev, "mixer auto=%d->%d val=%d->%d\n", + dev->mixer_gain_auto->cur.val, dev->mixer_gain_auto->val, + dev->mixer_gain->cur.val, dev->mixer_gain->val); - if (s->mixer_gain_auto->val) + if (dev->mixer_gain_auto->val) u8tmp = 0x15; else u8tmp = 0x14; - ret = regmap_write(s->regmap, 0x20, u8tmp); + ret = regmap_write(dev->regmap, 0x20, u8tmp); if (ret) goto err; - if (s->mixer_gain_auto->val == false) { - ret = regmap_write(s->regmap, 0x15, s->mixer_gain->val); + if (dev->mixer_gain_auto->val == false) { + ret = regmap_write(dev->regmap, 0x15, dev->mixer_gain->val); if (ret) goto err; } -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int e4000_set_if_gain(struct dvb_frontend *fe) { - struct e4000 *s = fe->tuner_priv; + struct e4000_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; u8 buf[2]; u8 u8tmp; - dev_dbg(&s->client->dev, "if auto=%d->%d val=%d->%d\n", - s->if_gain_auto->cur.val, s->if_gain_auto->val, - s->if_gain->cur.val, s->if_gain->val); + dev_dbg(&client->dev, "if auto=%d->%d val=%d->%d\n", + dev->if_gain_auto->cur.val, dev->if_gain_auto->val, + dev->if_gain->cur.val, dev->if_gain->val); - if (s->if_gain_auto->val && s->lna_gain_auto->cur.val) + if (dev->if_gain_auto->val && dev->lna_gain_auto->cur.val) u8tmp = 0x17; - else if (s->lna_gain_auto->cur.val) + else if (dev->lna_gain_auto->cur.val) u8tmp = 0x19; - else if (s->if_gain_auto->val) + else if (dev->if_gain_auto->val) u8tmp = 0x16; else u8tmp = 0x10; - ret = regmap_write(s->regmap, 0x1a, u8tmp); + ret = regmap_write(dev->regmap, 0x1a, u8tmp); if (ret) goto err; - if (s->if_gain_auto->val == false) { - buf[0] = e4000_if_gain_lut[s->if_gain->val].reg16_val; - buf[1] = e4000_if_gain_lut[s->if_gain->val].reg17_val; - ret = regmap_bulk_write(s->regmap, 0x16, buf, 2); + if (dev->if_gain_auto->val == false) { + buf[0] = e4000_if_gain_lut[dev->if_gain->val].reg16_val; + buf[1] = e4000_if_gain_lut[dev->if_gain->val].reg17_val; + ret = regmap_bulk_write(dev->regmap, 0x16, buf, 2); if (ret) goto err; } -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int e4000_pll_lock(struct dvb_frontend *fe) { - struct e4000 *s = fe->tuner_priv; + struct e4000_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; - unsigned int utmp; + unsigned int uitmp; - ret = regmap_read(s->regmap, 0x07, &utmp); + ret = regmap_read(dev->regmap, 0x07, &uitmp); if (ret) goto err; - s->pll_lock->val = (utmp & 0x01); -err: - if (ret) - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev->pll_lock->val = (uitmp & 0x01); + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl); + struct e4000_dev *dev = container_of(ctrl->handler, struct e4000_dev, hdl); + struct i2c_client *client = dev->client; int ret; - if (!s->active) + if (!dev->active) return 0; switch (ctrl->id) { case V4L2_CID_RF_TUNER_PLL_LOCK: - ret = e4000_pll_lock(s->fe); + ret = e4000_pll_lock(dev->fe); break; default: - dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n", - ctrl->id, ctrl->name); + dev_dbg(&client->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; } @@ -413,35 +544,39 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl) static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) { - struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl); - struct dvb_frontend *fe = s->fe; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct e4000_dev *dev = container_of(ctrl->handler, struct e4000_dev, hdl); + struct i2c_client *client = dev->client; int ret; - if (!s->active) + if (!dev->active) return 0; switch (ctrl->id) { case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH: - c->bandwidth_hz = s->bandwidth->val; - ret = e4000_set_params(s->fe); + /* + * TODO: Auto logic does not work 100% correctly as tuner driver + * do not have information to calculate maximum suitable + * bandwidth. Calculating it is responsible of master driver. + */ + dev->f_bandwidth = dev->bandwidth->val; + ret = e4000_set_params(dev); break; case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: case V4L2_CID_RF_TUNER_LNA_GAIN: - ret = e4000_set_lna_gain(s->fe); + ret = e4000_set_lna_gain(dev->fe); break; case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: case V4L2_CID_RF_TUNER_MIXER_GAIN: - ret = e4000_set_mixer_gain(s->fe); + ret = e4000_set_mixer_gain(dev->fe); break; case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: case V4L2_CID_RF_TUNER_IF_GAIN: - ret = e4000_set_if_gain(s->fe); + ret = e4000_set_if_gain(dev->fe); break; default: - dev_dbg(&s->client->dev, "unknown ctrl: id=%d name=%s\n", - ctrl->id, ctrl->name); + dev_dbg(&client->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; } @@ -454,157 +589,175 @@ static const struct v4l2_ctrl_ops e4000_ctrl_ops = { }; #endif -static const struct dvb_tuner_ops e4000_tuner_ops = { +/* + * DVB API + */ +static int e4000_dvb_set_params(struct dvb_frontend *fe) +{ + struct e4000_dev *dev = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + dev->f_frequency = c->frequency; + dev->f_bandwidth = c->bandwidth_hz; + return e4000_set_params(dev); +} + +static int e4000_dvb_init(struct dvb_frontend *fe) +{ + return e4000_init(fe->tuner_priv); +} + +static int e4000_dvb_sleep(struct dvb_frontend *fe) +{ + return e4000_sleep(fe->tuner_priv); +} + +static int e4000_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + return 0; +} + +static const struct dvb_tuner_ops e4000_dvb_tuner_ops = { .info = { .name = "Elonics E4000", .frequency_min = 174000000, .frequency_max = 862000000, }, - .init = e4000_init, - .sleep = e4000_sleep, - .set_params = e4000_set_params, + .init = e4000_dvb_init, + .sleep = e4000_dvb_sleep, + .set_params = e4000_dvb_set_params, - .get_if_frequency = e4000_get_if_frequency, + .get_if_frequency = e4000_dvb_get_if_frequency, }; -/* - * Use V4L2 subdev to carry V4L2 control handler, even we don't implement - * subdev itself, just to avoid reinventing the wheel. - */ static int e4000_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { + struct e4000_dev *dev; struct e4000_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; - struct e4000 *s; int ret; - unsigned int utmp; + unsigned int uitmp; static const struct regmap_config regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = 0xff, }; - s = kzalloc(sizeof(struct e4000), GFP_KERNEL); - if (!s) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; - dev_err(&client->dev, "kzalloc() failed\n"); goto err; } - s->clock = cfg->clock; - s->client = client; - s->fe = cfg->fe; - s->regmap = devm_regmap_init_i2c(client, ®map_config); - if (IS_ERR(s->regmap)) { - ret = PTR_ERR(s->regmap); - goto err; + dev->clk = cfg->clock; + dev->client = client; + dev->fe = cfg->fe; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; } /* check if the tuner is there */ - ret = regmap_read(s->regmap, 0x02, &utmp); + ret = regmap_read(dev->regmap, 0x02, &uitmp); if (ret) - goto err; + goto err_kfree; - dev_dbg(&s->client->dev, "chip id=%02x\n", utmp); + dev_dbg(&client->dev, "chip id=%02x\n", uitmp); - if (utmp != 0x40) { + if (uitmp != 0x40) { ret = -ENODEV; - goto err; + goto err_kfree; } /* put sleep as chip seems to be in normal mode by default */ - ret = regmap_write(s->regmap, 0x00, 0x00); + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) - goto err; + goto err_kfree; #if IS_ENABLED(CONFIG_VIDEO_V4L2) /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 9); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + v4l2_ctrl_handler_init(&dev->hdl, 9); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); - s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + dev->lna_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1); - s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10); - v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false); - s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + v4l2_ctrl_auto_cluster(2, &dev->lna_gain_auto, 0, false); + dev->mixer_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1); - s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + dev->mixer_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false); - s->if_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + v4l2_ctrl_auto_cluster(2, &dev->mixer_gain_auto, 0, false); + dev->if_gain_auto = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1); - s->if_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0); - v4l2_ctrl_auto_cluster(2, &s->if_gain_auto, 0, false); - s->pll_lock = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops, + v4l2_ctrl_auto_cluster(2, &dev->if_gain_auto, 0, false); + dev->pll_lock = v4l2_ctrl_new_std(&dev->hdl, &e4000_ctrl_ops, V4L2_CID_RF_TUNER_PLL_LOCK, 0, 1, 1, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->client->dev, "Could not initialize controls\n"); - v4l2_ctrl_handler_free(&s->hdl); - goto err; + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(&client->dev, "Could not initialize controls\n"); + v4l2_ctrl_handler_free(&dev->hdl); + goto err_kfree; } - s->sd.ctrl_handler = &s->hdl; + dev->sd.ctrl_handler = &dev->hdl; + dev->f_frequency = bands[0].rangelow; + dev->f_bandwidth = dev->bandwidth->val; + v4l2_i2c_subdev_init(&dev->sd, client, &e4000_subdev_ops); #endif + fe->tuner_priv = dev; + memcpy(&fe->ops.tuner_ops, &e4000_dvb_tuner_ops, + sizeof(fe->ops.tuner_ops)); + v4l2_set_subdevdata(&dev->sd, client); + i2c_set_clientdata(client, &dev->sd); - dev_info(&s->client->dev, "Elonics E4000 successfully identified\n"); - - fe->tuner_priv = s; - memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - v4l2_set_subdevdata(&s->sd, client); - i2c_set_clientdata(client, &s->sd); - + dev_info(&client->dev, "Elonics E4000 successfully identified\n"); return 0; +err_kfree: + kfree(dev); err: - if (ret) { - dev_dbg(&client->dev, "failed=%d\n", ret); - kfree(s); - } - + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int e4000_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct e4000 *s = container_of(sd, struct e4000, sd); - struct dvb_frontend *fe = s->fe; + struct e4000_dev *dev = container_of(sd, struct e4000_dev, sd); dev_dbg(&client->dev, "\n"); #if IS_ENABLED(CONFIG_VIDEO_V4L2) - v4l2_ctrl_handler_free(&s->hdl); + v4l2_ctrl_handler_free(&dev->hdl); #endif - memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = NULL; - kfree(s); + kfree(dev); return 0; } -static const struct i2c_device_id e4000_id[] = { +static const struct i2c_device_id e4000_id_table[] = { {"e4000", 0}, {} }; -MODULE_DEVICE_TABLE(i2c, e4000_id); +MODULE_DEVICE_TABLE(i2c, e4000_id_table); static struct i2c_driver e4000_driver = { .driver = { - .owner = THIS_MODULE, .name = "e4000", + .suppress_bind_attrs = true, }, .probe = e4000_probe, .remove = e4000_remove, - .id_table = e4000_id, + .id_table = e4000_id_table, }; module_i2c_driver(e4000_driver); diff --git a/kernel/drivers/media/tuners/e4000.h b/kernel/drivers/media/tuners/e4000.h index e74b8b2f2..aa9340c05 100644 --- a/kernel/drivers/media/tuners/e4000.h +++ b/kernel/drivers/media/tuners/e4000.h @@ -21,7 +21,6 @@ #ifndef E4000_H #define E4000_H -#include #include "dvb_frontend.h" /* diff --git a/kernel/drivers/media/tuners/e4000_priv.h b/kernel/drivers/media/tuners/e4000_priv.h index cb0070483..d6d5d11bb 100644 --- a/kernel/drivers/media/tuners/e4000_priv.h +++ b/kernel/drivers/media/tuners/e4000_priv.h @@ -22,17 +22,20 @@ #define E4000_PRIV_H #include "e4000.h" +#include #include #include #include -struct e4000 { +struct e4000_dev { struct i2c_client *client; struct regmap *regmap; - u32 clock; + u32 clk; struct dvb_frontend *fe; struct v4l2_subdev sd; bool active; + unsigned int f_frequency; + unsigned int f_bandwidth; /* Controls */ struct v4l2_ctrl_handler hdl; @@ -49,8 +52,8 @@ struct e4000 { struct e4000_pll { u32 freq; - u8 div; - u8 mul; + u8 div_out_reg; + u8 div_out; }; static const struct e4000_pll e4000_pll_lut[] = { diff --git a/kernel/drivers/media/tuners/fc0013.c b/kernel/drivers/media/tuners/fc0013.c index b41623157..522690d97 100644 --- a/kernel/drivers/media/tuners/fc0013.c +++ b/kernel/drivers/media/tuners/fc0013.c @@ -217,8 +217,6 @@ static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) } else { /* UHF and GPS */ ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); } - if (ret) - goto error_out; error_out: return ret; } diff --git a/kernel/drivers/media/tuners/fc2580.c b/kernel/drivers/media/tuners/fc2580.c index f0c9c4286..f4d4665de 100644 --- a/kernel/drivers/media/tuners/fc2580.c +++ b/kernel/drivers/media/tuners/fc2580.c @@ -20,535 +20,627 @@ #include "fc2580_priv.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - /* * TODO: * I2C write and read works only for one single register. Multiple registers * could not be accessed using normal register address auto-increment. * There could be (very likely) register to change that behavior.... - * - * Due to that limitation functions: - * fc2580_wr_regs() - * fc2580_rd_regs() - * could not be used for accessing more than one register at once. - * - * TODO: - * Currently it blind writes bunch of static registers from the - * fc2580_freq_regs_lut[] when fc2580_set_params() is called. Add some - * logic to reduce unneeded register writes. */ -/* write multiple registers */ -static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (1 + 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; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple registers */ -static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg->i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c rd reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write single register */ -static int fc2580_wr_reg(struct fc2580_priv *priv, u8 reg, u8 val) -{ - return fc2580_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -static int fc2580_rd_reg(struct fc2580_priv *priv, u8 reg, u8 *val) -{ - return fc2580_rd_regs(priv, reg, val, 1); -} - /* write single register conditionally only when value differs from 0xff * XXX: This is special routine meant only for writing fc2580_freq_regs_lut[] * values. Do not use for the other purposes. */ -static int fc2580_wr_reg_ff(struct fc2580_priv *priv, u8 reg, u8 val) +static int fc2580_wr_reg_ff(struct fc2580_dev *dev, u8 reg, u8 val) { if (val == 0xff) return 0; else - return fc2580_wr_regs(priv, reg, &val, 1); + return regmap_write(dev->regmap, reg, val); } -static int fc2580_set_params(struct dvb_frontend *fe) +static int fc2580_set_params(struct fc2580_dev *dev) { - struct fc2580_priv *priv = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret = 0, i; - unsigned int r_val, n_val, k_val, k_val_reg, f_ref; - u8 tmp_val, r18_val; + struct i2c_client *client = dev->client; + int ret, i; + unsigned int uitmp, div_ref, div_ref_val, div_n, k, k_cw, div_out; u64 f_vco; + u8 synth_config; + unsigned long timeout; + + if (!dev->active) { + dev_dbg(&client->dev, "tuner is sleeping\n"); + return 0; + } /* - * Fractional-N synthesizer/PLL. - * Most likely all those PLL calculations are not correct. I am not - * sure, but it looks like it is divider based Fractional-N synthesizer. - * There is divider for reference clock too? - * Anyhow, synthesizer calculation results seems to be quite correct. + * Fractional-N synthesizer + * + * +---------------------------------------+ + * v | + * Fref +----+ +----+ +-------+ +----+ +------+ +---+ + * ------> | /R | --> | PD | --> | VCO | ------> | /2 | --> | /N.F | <-- | K | + * +----+ +----+ +-------+ +----+ +------+ +---+ + * | + * | + * v + * +-------+ Fout + * | /Rout | ------> + * +-------+ */ - - dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ - "bandwidth_hz=%d\n", __func__, - c->delivery_system, c->frequency, c->bandwidth_hz); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - /* PLL */ for (i = 0; i < ARRAY_SIZE(fc2580_pll_lut); i++) { - if (c->frequency <= fc2580_pll_lut[i].freq) + if (dev->f_frequency <= fc2580_pll_lut[i].freq) break; } - - if (i == ARRAY_SIZE(fc2580_pll_lut)) + if (i == ARRAY_SIZE(fc2580_pll_lut)) { + ret = -EINVAL; goto err; + } - f_vco = c->frequency; - f_vco *= fc2580_pll_lut[i].div; - - if (f_vco >= 2600000000UL) - tmp_val = 0x0e | fc2580_pll_lut[i].band; + #define DIV_PRE_N 2 + #define F_REF dev->clk + div_out = fc2580_pll_lut[i].div_out; + f_vco = (u64) dev->f_frequency * div_out; + synth_config = fc2580_pll_lut[i].band; + if (f_vco < 2600000000ULL) + synth_config |= 0x06; else - tmp_val = 0x06 | fc2580_pll_lut[i].band; - - ret = fc2580_wr_reg(priv, 0x02, tmp_val); - if (ret < 0) - goto err; - - if (f_vco >= 2UL * 76 * priv->cfg->clock) { - r_val = 1; - r18_val = 0x00; - } else if (f_vco >= 1UL * 76 * priv->cfg->clock) { - r_val = 2; - r18_val = 0x10; + synth_config |= 0x0e; + + /* select reference divider R (keep PLL div N in valid range) */ + #define DIV_N_MIN 76 + if (f_vco >= div_u64((u64) DIV_PRE_N * DIV_N_MIN * F_REF, 1)) { + div_ref = 1; + div_ref_val = 0x00; + } else if (f_vco >= div_u64((u64) DIV_PRE_N * DIV_N_MIN * F_REF, 2)) { + div_ref = 2; + div_ref_val = 0x10; } else { - r_val = 4; - r18_val = 0x20; + div_ref = 4; + div_ref_val = 0x20; } - f_ref = 2UL * priv->cfg->clock / r_val; - n_val = div_u64_rem(f_vco, f_ref, &k_val); - k_val_reg = div_u64(1ULL * k_val * (1 << 20), f_ref); + /* calculate PLL integer and fractional control word */ + uitmp = DIV_PRE_N * F_REF / div_ref; + div_n = div_u64_rem(f_vco, uitmp, &k); + k_cw = div_u64((u64) k * 0x100000, uitmp); - ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff)); - if (ret < 0) - goto err; + dev_dbg(&client->dev, + "frequency=%u bandwidth=%u f_vco=%llu F_REF=%u div_ref=%u div_n=%u k=%u div_out=%u k_cw=%0x\n", + dev->f_frequency, dev->f_bandwidth, f_vco, F_REF, div_ref, + div_n, k, div_out, k_cw); - ret = fc2580_wr_reg(priv, 0x1a, (k_val_reg >> 8) & 0xff); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x02, synth_config); + if (ret) goto err; - ret = fc2580_wr_reg(priv, 0x1b, (k_val_reg >> 0) & 0xff); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x18, div_ref_val << 0 | k_cw >> 16); + if (ret) goto err; - ret = fc2580_wr_reg(priv, 0x1c, n_val); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x1a, (k_cw >> 8) & 0xff); + if (ret) goto err; - if (priv->cfg->clock >= 28000000) { - ret = fc2580_wr_reg(priv, 0x4b, 0x22); - if (ret < 0) - goto err; - } - - if (fc2580_pll_lut[i].band == 0x00) { - if (c->frequency <= 794000000) - tmp_val = 0x9f; - else - tmp_val = 0x8f; + ret = regmap_write(dev->regmap, 0x1b, (k_cw >> 0) & 0xff); + if (ret) + goto err; - ret = fc2580_wr_reg(priv, 0x2d, tmp_val); - if (ret < 0) - goto err; - } + ret = regmap_write(dev->regmap, 0x1c, div_n); + if (ret) + goto err; /* registers */ for (i = 0; i < ARRAY_SIZE(fc2580_freq_regs_lut); i++) { - if (c->frequency <= fc2580_freq_regs_lut[i].freq) + if (dev->f_frequency <= fc2580_freq_regs_lut[i].freq) break; } - - if (i == ARRAY_SIZE(fc2580_freq_regs_lut)) + if (i == ARRAY_SIZE(fc2580_freq_regs_lut)) { + ret = -EINVAL; goto err; + } - ret = fc2580_wr_reg_ff(priv, 0x25, fc2580_freq_regs_lut[i].r25_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x25, fc2580_freq_regs_lut[i].r25_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x27, fc2580_freq_regs_lut[i].r27_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x27, fc2580_freq_regs_lut[i].r27_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x28, fc2580_freq_regs_lut[i].r28_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x28, fc2580_freq_regs_lut[i].r28_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x29, fc2580_freq_regs_lut[i].r29_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x29, fc2580_freq_regs_lut[i].r29_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x2b, fc2580_freq_regs_lut[i].r2b_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x2b, fc2580_freq_regs_lut[i].r2b_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x2c, fc2580_freq_regs_lut[i].r2c_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x2c, fc2580_freq_regs_lut[i].r2c_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x2d, fc2580_freq_regs_lut[i].r2d_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x2d, fc2580_freq_regs_lut[i].r2d_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x30, fc2580_freq_regs_lut[i].r30_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x30, fc2580_freq_regs_lut[i].r30_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x44, fc2580_freq_regs_lut[i].r44_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x44, fc2580_freq_regs_lut[i].r44_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x50, fc2580_freq_regs_lut[i].r50_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x50, fc2580_freq_regs_lut[i].r50_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x53, fc2580_freq_regs_lut[i].r53_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x53, fc2580_freq_regs_lut[i].r53_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x5f, fc2580_freq_regs_lut[i].r5f_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x5f, fc2580_freq_regs_lut[i].r5f_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x61, fc2580_freq_regs_lut[i].r61_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x61, fc2580_freq_regs_lut[i].r61_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x62, fc2580_freq_regs_lut[i].r62_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x62, fc2580_freq_regs_lut[i].r62_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x63, fc2580_freq_regs_lut[i].r63_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x63, fc2580_freq_regs_lut[i].r63_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x67, fc2580_freq_regs_lut[i].r67_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x67, fc2580_freq_regs_lut[i].r67_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x68, fc2580_freq_regs_lut[i].r68_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x68, fc2580_freq_regs_lut[i].r68_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x69, fc2580_freq_regs_lut[i].r69_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x69, fc2580_freq_regs_lut[i].r69_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6a, fc2580_freq_regs_lut[i].r6a_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6a, fc2580_freq_regs_lut[i].r6a_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6b, fc2580_freq_regs_lut[i].r6b_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6b, fc2580_freq_regs_lut[i].r6b_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6c, fc2580_freq_regs_lut[i].r6c_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6c, fc2580_freq_regs_lut[i].r6c_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6d, fc2580_freq_regs_lut[i].r6d_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6d, fc2580_freq_regs_lut[i].r6d_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6e, fc2580_freq_regs_lut[i].r6e_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6e, fc2580_freq_regs_lut[i].r6e_val); + if (ret) goto err; - ret = fc2580_wr_reg_ff(priv, 0x6f, fc2580_freq_regs_lut[i].r6f_val); - if (ret < 0) + ret = fc2580_wr_reg_ff(dev, 0x6f, fc2580_freq_regs_lut[i].r6f_val); + if (ret) goto err; /* IF filters */ for (i = 0; i < ARRAY_SIZE(fc2580_if_filter_lut); i++) { - if (c->bandwidth_hz <= fc2580_if_filter_lut[i].freq) + if (dev->f_bandwidth <= fc2580_if_filter_lut[i].freq) break; } - - if (i == ARRAY_SIZE(fc2580_if_filter_lut)) - goto err; - - ret = fc2580_wr_reg(priv, 0x36, fc2580_if_filter_lut[i].r36_val); - if (ret < 0) + if (i == ARRAY_SIZE(fc2580_if_filter_lut)) { + ret = -EINVAL; goto err; + } - ret = fc2580_wr_reg(priv, 0x37, div_u64(1ULL * priv->cfg->clock * - fc2580_if_filter_lut[i].mul, 1000000000)); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x36, fc2580_if_filter_lut[i].r36_val); + if (ret) goto err; - ret = fc2580_wr_reg(priv, 0x39, fc2580_if_filter_lut[i].r39_val); - if (ret < 0) + uitmp = (unsigned int) 8058000 - (dev->f_bandwidth * 122 / 100 / 2); + uitmp = div64_u64((u64) dev->clk * uitmp, 1000000000000ULL); + ret = regmap_write(dev->regmap, 0x37, uitmp); + if (ret) goto err; - /* calibration? */ - ret = fc2580_wr_reg(priv, 0x2e, 0x09); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x39, fc2580_if_filter_lut[i].r39_val); + if (ret) goto err; - for (i = 0; i < 5; i++) { - ret = fc2580_rd_reg(priv, 0x2f, &tmp_val); - if (ret < 0) + timeout = jiffies + msecs_to_jiffies(30); + for (uitmp = ~0xc0; !time_after(jiffies, timeout) && uitmp != 0xc0;) { + /* trigger filter */ + ret = regmap_write(dev->regmap, 0x2e, 0x09); + if (ret) goto err; - /* done when [7:6] are set */ - if ((tmp_val & 0xc0) == 0xc0) - break; - - ret = fc2580_wr_reg(priv, 0x2e, 0x01); - if (ret < 0) + /* locked when [7:6] are set (val: d7 6MHz, d5 7MHz, cd 8MHz) */ + ret = regmap_read(dev->regmap, 0x2f, &uitmp); + if (ret) goto err; + uitmp &= 0xc0; - ret = fc2580_wr_reg(priv, 0x2e, 0x09); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x2e, 0x01); + if (ret) goto err; - - usleep_range(5000, 25000); } - - dev_dbg(&priv->i2c->dev, "%s: loop=%i\n", __func__, i); - - ret = fc2580_wr_reg(priv, 0x2e, 0x01); - if (ret < 0) - goto err; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + if (uitmp != 0xc0) + dev_dbg(&client->dev, "filter did not lock %02x\n", uitmp); return 0; err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int fc2580_init(struct dvb_frontend *fe) +static int fc2580_init(struct fc2580_dev *dev) { - struct fc2580_priv *priv = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret, i; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + dev_dbg(&client->dev, "\n"); for (i = 0; i < ARRAY_SIZE(fc2580_init_reg_vals); i++) { - ret = fc2580_wr_reg(priv, fc2580_init_reg_vals[i].reg, + ret = regmap_write(dev->regmap, fc2580_init_reg_vals[i].reg, fc2580_init_reg_vals[i].val); - if (ret < 0) + if (ret) goto err; } - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - + dev->active = true; return 0; err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int fc2580_sleep(struct dvb_frontend *fe) +static int fc2580_sleep(struct fc2580_dev *dev) { - struct fc2580_priv *priv = fe->tuner_priv; + struct i2c_client *client = dev->client; int ret; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + dev->active = false; - ret = fc2580_wr_reg(priv, 0x02, 0x0a); - if (ret < 0) + ret = regmap_write(dev->regmap, 0x02, 0x0a); + if (ret) goto err; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return 0; err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int fc2580_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +/* + * DVB API + */ +static int fc2580_dvb_set_params(struct dvb_frontend *fe) { - struct fc2580_priv *priv = fe->tuner_priv; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - - *frequency = 0; /* Zero-IF */ + struct fc2580_dev *dev = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - return 0; + dev->f_frequency = c->frequency; + dev->f_bandwidth = c->bandwidth_hz; + return fc2580_set_params(dev); } -static int fc2580_release(struct dvb_frontend *fe) +static int fc2580_dvb_init(struct dvb_frontend *fe) { - struct fc2580_priv *priv = fe->tuner_priv; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + return fc2580_init(fe->tuner_priv); +} - kfree(fe->tuner_priv); +static int fc2580_dvb_sleep(struct dvb_frontend *fe) +{ + return fc2580_sleep(fe->tuner_priv); +} +static int fc2580_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ return 0; } -static const struct dvb_tuner_ops fc2580_tuner_ops = { +static const struct dvb_tuner_ops fc2580_dvb_tuner_ops = { .info = { .name = "FCI FC2580", .frequency_min = 174000000, .frequency_max = 862000000, }, - .release = fc2580_release, + .init = fc2580_dvb_init, + .sleep = fc2580_dvb_sleep, + .set_params = fc2580_dvb_set_params, + + .get_if_frequency = fc2580_dvb_get_if_frequency, +}; + +/* + * V4L2 API + */ +#if IS_ENABLED(CONFIG_VIDEO_V4L2) +static const struct v4l2_frequency_band bands[] = { + { + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 130000000, + .rangehigh = 2000000000, + }, +}; + +static inline struct fc2580_dev *fc2580_subdev_to_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct fc2580_dev, subdev); +} + +static int fc2580_s_power(struct v4l2_subdev *sd, int on) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "on=%d\n", on); + + if (on) + ret = fc2580_init(dev); + else + ret = fc2580_sleep(dev); + if (ret) + return ret; + + return fc2580_set_params(dev); +} + +static const struct v4l2_subdev_core_ops fc2580_subdev_core_ops = { + .s_power = fc2580_s_power, +}; + +static int fc2580_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "index=%d\n", v->index); + + strlcpy(v->name, "FCI FC2580", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = bands[0].rangelow; + v->rangehigh = bands[0].rangehigh; + return 0; +} + +static int fc2580_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "index=%d\n", v->index); + return 0; +} + +static int fc2580_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "tuner=%d\n", f->tuner); + f->frequency = dev->f_frequency; + return 0; +} + +static int fc2580_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *f) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); + + dev->f_frequency = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, bands[0].rangehigh); + return fc2580_set_params(dev); +} + +static int fc2580_enum_freq_bands(struct v4l2_subdev *sd, + struct v4l2_frequency_band *band) +{ + struct fc2580_dev *dev = fc2580_subdev_to_dev(sd); + struct i2c_client *client = dev->client; + + dev_dbg(&client->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); - .init = fc2580_init, - .sleep = fc2580_sleep, - .set_params = fc2580_set_params, + if (band->index >= ARRAY_SIZE(bands)) + return -EINVAL; - .get_if_frequency = fc2580_get_if_frequency, + band->capability = bands[band->index].capability; + band->rangelow = bands[band->index].rangelow; + band->rangehigh = bands[band->index].rangehigh; + return 0; +} + +static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = { + .g_tuner = fc2580_g_tuner, + .s_tuner = fc2580_s_tuner, + .g_frequency = fc2580_g_frequency, + .s_frequency = fc2580_s_frequency, + .enum_freq_bands = fc2580_enum_freq_bands, +}; + +static const struct v4l2_subdev_ops fc2580_subdev_ops = { + .core = &fc2580_subdev_core_ops, + .tuner = &fc2580_subdev_tuner_ops, }; -struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct fc2580_config *cfg) +static int fc2580_s_ctrl(struct v4l2_ctrl *ctrl) { - struct fc2580_priv *priv; + struct fc2580_dev *dev = container_of(ctrl->handler, struct fc2580_dev, hdl); + struct i2c_client *client = dev->client; int ret; - u8 chip_id; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + dev_dbg(&client->dev, "ctrl: id=%d name=%s cur.val=%d val=%d\n", + ctrl->id, ctrl->name, ctrl->cur.val, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + /* + * TODO: Auto logic does not work 100% correctly as tuner driver + * do not have information to calculate maximum suitable + * bandwidth. Calculating it is responsible of master driver. + */ + dev->f_bandwidth = dev->bandwidth->val; + ret = fc2580_set_params(dev); + break; + default: + dev_dbg(&client->dev, "unknown ctrl"); + ret = -EINVAL; + } + return ret; +} + +static const struct v4l2_ctrl_ops fc2580_ctrl_ops = { + .s_ctrl = fc2580_s_ctrl, +}; +#endif + +static struct v4l2_subdev *fc2580_get_v4l2_subdev(struct i2c_client *client) +{ + struct fc2580_dev *dev = i2c_get_clientdata(client); + + if (dev->subdev.ops) + return &dev->subdev; + else + return NULL; +} + +static int fc2580_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fc2580_dev *dev; + struct fc2580_platform_data *pdata = client->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; + int ret; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; - priv = kzalloc(sizeof(struct fc2580_priv), GFP_KERNEL); - if (!priv) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; - dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); goto err; } - priv->cfg = cfg; - priv->i2c = i2c; + if (pdata->clk) + dev->clk = pdata->clk; + else + dev->clk = 16384000; /* internal clock */ + dev->client = client; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } /* check if the tuner is there */ - ret = fc2580_rd_reg(priv, 0x01, &chip_id); - if (ret < 0) - goto err; + ret = regmap_read(dev->regmap, 0x01, &uitmp); + if (ret) + goto err_kfree; - dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + dev_dbg(&client->dev, "chip_id=%02x\n", uitmp); - switch (chip_id) { + switch (uitmp) { case 0x56: case 0x5a: break; default: - goto err; + ret = -ENODEV; + goto err_kfree; } - dev_info(&priv->i2c->dev, - "%s: FCI FC2580 successfully identified\n", - KBUILD_MODNAME); - - fe->tuner_priv = priv; - memcpy(&fe->ops.tuner_ops, &fc2580_tuner_ops, - sizeof(struct dvb_tuner_ops)); +#if IS_ENABLED(CONFIG_VIDEO_V4L2) + /* Register controls */ + v4l2_ctrl_handler_init(&dev->hdl, 2); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &fc2580_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &fc2580_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 3000, 10000000, 1, 3000); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(&client->dev, "Could not initialize controls\n"); + v4l2_ctrl_handler_free(&dev->hdl); + goto err_kfree; + } + dev->subdev.ctrl_handler = &dev->hdl; + dev->f_frequency = bands[0].rangelow; + dev->f_bandwidth = dev->bandwidth->val; + v4l2_i2c_subdev_init(&dev->subdev, client, &fc2580_subdev_ops); +#endif + fe->tuner_priv = dev; + memcpy(&fe->ops.tuner_ops, &fc2580_dvb_tuner_ops, + sizeof(fe->ops.tuner_ops)); + pdata->get_v4l2_subdev = fc2580_get_v4l2_subdev; + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "FCI FC2580 successfully identified\n"); + return 0; +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); +static int fc2580_remove(struct i2c_client *client) +{ + struct fc2580_dev *dev = i2c_get_clientdata(client); - return fe; -err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + dev_dbg(&client->dev, "\n"); - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; +#if IS_ENABLED(CONFIG_VIDEO_V4L2) + v4l2_ctrl_handler_free(&dev->hdl); +#endif + kfree(dev); + return 0; } -EXPORT_SYMBOL(fc2580_attach); + +static const struct i2c_device_id fc2580_id_table[] = { + {"fc2580", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, fc2580_id_table); + +static struct i2c_driver fc2580_driver = { + .driver = { + .name = "fc2580", + .suppress_bind_attrs = true, + }, + .probe = fc2580_probe, + .remove = fc2580_remove, + .id_table = fc2580_id_table, +}; + +module_i2c_driver(fc2580_driver); MODULE_DESCRIPTION("FCI FC2580 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); diff --git a/kernel/drivers/media/tuners/fc2580.h b/kernel/drivers/media/tuners/fc2580.h index b1ce6770f..862ea4699 100644 --- a/kernel/drivers/media/tuners/fc2580.h +++ b/kernel/drivers/media/tuners/fc2580.h @@ -21,32 +21,26 @@ #ifndef FC2580_H #define FC2580_H -#include #include "dvb_frontend.h" +#include +#include -struct fc2580_config { - /* - * I2C address - * 0x56, ... - */ - u8 i2c_addr; +/* + * I2C address + * 0x56, ... + */ - /* - * clock - */ - u32 clock; -}; +/** + * struct fc2580_platform_data - Platform data for the fc2580 driver + * @clk: Clock frequency (0 = internal clock). + * @dvb_frontend: DVB frontend. + * @get_v4l2_subdev: Get V4L2 subdev. + */ +struct fc2580_platform_data { + u32 clk; + struct dvb_frontend *dvb_frontend; -#if IS_REACHABLE(CONFIG_MEDIA_TUNER_FC2580) -extern struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct fc2580_config *cfg); -#else -static inline struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct fc2580_config *cfg) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif + struct v4l2_subdev* (*get_v4l2_subdev)(struct i2c_client *); +}; #endif diff --git a/kernel/drivers/media/tuners/fc2580_priv.h b/kernel/drivers/media/tuners/fc2580_priv.h index 646c99452..031a43d7e 100644 --- a/kernel/drivers/media/tuners/fc2580_priv.h +++ b/kernel/drivers/media/tuners/fc2580_priv.h @@ -22,6 +22,9 @@ #define FC2580_PRIV_H #include "fc2580.h" +#include +#include +#include #include struct fc2580_reg_val { @@ -50,7 +53,7 @@ static const struct fc2580_reg_val fc2580_init_reg_vals[] = { struct fc2580_pll { u32 freq; - u8 div; + u8 div_out; u8 band; }; @@ -63,16 +66,15 @@ static const struct fc2580_pll fc2580_pll_lut[] = { struct fc2580_if_filter { u32 freq; - u16 mul; u8 r36_val; u8 r39_val; }; static const struct fc2580_if_filter fc2580_if_filter_lut[] = { - { 6000000, 4400, 0x18, 0x00}, - { 7000000, 3910, 0x18, 0x80}, - { 8000000, 3300, 0x18, 0x80}, - {0xffffffff, 3300, 0x18, 0x80}, + { 6000000, 0x18, 0x00}, + { 7000000, 0x18, 0x80}, + { 8000000, 0x18, 0x80}, + {0xffffffff, 0x18, 0x80}, }; struct fc2580_freq_regs { @@ -110,15 +112,15 @@ static const struct fc2580_freq_regs fc2580_freq_regs_lut[] = { 0x50, 0x0f, 0x07, 0x00, 0x15, 0x03, 0x05, 0x10, 0x12, 0x08, 0x0a, 0x78, 0x32, 0x54}, { 538000000, - 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c, + 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0x9f, 0x09, 0xff, 0x8c, 0x50, 0x13, 0x07, 0x06, 0x15, 0x06, 0x08, 0x10, 0x12, 0x0b, 0x0c, 0x78, 0x32, 0x14}, { 794000000, - 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c, + 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0x9f, 0x09, 0xff, 0x8c, 0x50, 0x15, 0x03, 0x03, 0x15, 0x03, 0x05, 0x0c, 0x0e, 0x0b, 0x0c, 0x78, 0x32, 0x14}, {1000000000, - 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c, + 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0x8f, 0x09, 0xff, 0x8c, 0x50, 0x15, 0x07, 0x06, 0x15, 0x07, 0x09, 0x10, 0x12, 0x0b, 0x0c, 0x78, 0x32, 0x14}, {0xffffffff, @@ -127,9 +129,19 @@ static const struct fc2580_freq_regs fc2580_freq_regs_lut[] = { 0x0a, 0xa0, 0x50, 0x14}, }; -struct fc2580_priv { - const struct fc2580_config *cfg; - struct i2c_adapter *i2c; +struct fc2580_dev { + u32 clk; + struct i2c_client *client; + struct regmap *regmap; + struct v4l2_subdev subdev; + bool active; + unsigned int f_frequency; + unsigned int f_bandwidth; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; }; #endif diff --git a/kernel/drivers/media/tuners/it913x.c b/kernel/drivers/media/tuners/it913x.c index a076c87ed..5c96da693 100644 --- a/kernel/drivers/media/tuners/it913x.c +++ b/kernel/drivers/media/tuners/it913x.c @@ -463,7 +463,6 @@ MODULE_DEVICE_TABLE(i2c, it913x_id_table); static struct i2c_driver it913x_driver = { .driver = { - .owner = THIS_MODULE, .name = "it913x", }, .probe = it913x_probe, diff --git a/kernel/drivers/media/tuners/m88rs6000t.c b/kernel/drivers/media/tuners/m88rs6000t.c index d4c13fe6e..504bfbc40 100644 --- a/kernel/drivers/media/tuners/m88rs6000t.c +++ b/kernel/drivers/media/tuners/m88rs6000t.c @@ -729,7 +729,6 @@ MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); static struct i2c_driver m88rs6000t_driver = { .driver = { - .owner = THIS_MODULE, .name = "m88rs6000t", }, .probe = m88rs6000t_probe, diff --git a/kernel/drivers/media/tuners/msi001.c b/kernel/drivers/media/tuners/msi001.c index 74cfc3c98..3a12ef356 100644 --- a/kernel/drivers/media/tuners/msi001.c +++ b/kernel/drivers/media/tuners/msi001.c @@ -36,7 +36,7 @@ static const struct v4l2_frequency_band bands[] = { }, }; -struct msi001 { +struct msi001_dev { struct spi_device *spi; struct v4l2_subdev sd; @@ -51,25 +51,26 @@ struct msi001 { unsigned int f_tuner; }; -static inline struct msi001 *sd_to_msi001(struct v4l2_subdev *sd) +static inline struct msi001_dev *sd_to_msi001_dev(struct v4l2_subdev *sd) { - return container_of(sd, struct msi001, sd); + return container_of(sd, struct msi001_dev, sd); } -static int msi001_wreg(struct msi001 *s, u32 data) +static int msi001_wreg(struct msi001_dev *dev, u32 data) { /* Register format: 4 bits addr + 20 bits value */ - return spi_write(s->spi, &data, 3); + return spi_write(dev->spi, &data, 3); }; -static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, - int if_gain) +static int msi001_set_gain(struct msi001_dev *dev, int lna_gain, int mixer_gain, + int if_gain) { + struct spi_device *spi = dev->spi; int ret; u32 reg; - dev_dbg(&s->spi->dev, "lna=%d mixer=%d if=%d\n", - lna_gain, mixer_gain, if_gain); + dev_dbg(&spi->dev, "lna=%d mixer=%d if=%d\n", + lna_gain, mixer_gain, if_gain); reg = 1 << 0; reg |= (59 - if_gain) << 4; @@ -78,28 +79,29 @@ static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, reg |= (1 - lna_gain) << 13; reg |= 4 << 14; reg |= 0 << 17; - ret = msi001_wreg(s, reg); + ret = msi001_wreg(dev, reg); if (ret) goto err; return 0; err: - dev_dbg(&s->spi->dev, "failed %d\n", ret); + dev_dbg(&spi->dev, "failed %d\n", ret); return ret; }; -static int msi001_set_tuner(struct msi001 *s) +static int msi001_set_tuner(struct msi001_dev *dev) { + struct spi_device *spi = dev->spi; int ret, i; - unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; + unsigned int uitmp, div_n, k, k_thresh, k_frac, div_lo, f_if1; u32 reg; - u64 f_vco, tmp64; - u8 mode, filter_mode, lo_div; + u64 f_vco; + u8 mode, filter_mode; static const struct { u32 rf; u8 mode; - u8 lo_div; + u8 div_lo; } band_lut[] = { { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */ {108000000, 0x42, 32}, /* VHF_MODE */ @@ -130,7 +132,7 @@ static int msi001_set_tuner(struct msi001 *s) {8000000, 0x07}, /* 8 MHz */ }; - unsigned int f_rf = s->f_tuner; + unsigned int f_rf = dev->f_tuner; /* * bandwidth (Hz) @@ -144,19 +146,18 @@ static int msi001_set_tuner(struct msi001 *s) */ unsigned int f_if = 0; #define F_REF 24000000 - #define R_REF 4 - #define F_OUT_STEP 1 + #define DIV_PRE_N 4 + #define F_VCO_STEP div_lo - dev_dbg(&s->spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if); + dev_dbg(&spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if); for (i = 0; i < ARRAY_SIZE(band_lut); i++) { if (f_rf <= band_lut[i].rf) { mode = band_lut[i].mode; - lo_div = band_lut[i].lo_div; + div_lo = band_lut[i].div_lo; break; } } - if (i == ARRAY_SIZE(band_lut)) { ret = -EINVAL; goto err; @@ -174,14 +175,13 @@ static int msi001_set_tuner(struct msi001 *s) break; } } - if (i == ARRAY_SIZE(if_freq_lut)) { ret = -EINVAL; goto err; } /* filters */ - bandwidth = s->bandwidth->val; + bandwidth = dev->bandwidth->val; bandwidth = clamp(bandwidth, 200000U, 8000000U); for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { @@ -190,48 +190,61 @@ static int msi001_set_tuner(struct msi001 *s) break; } } - if (i == ARRAY_SIZE(bandwidth_lut)) { ret = -EINVAL; goto err; } - s->bandwidth->val = bandwidth_lut[i].freq; + dev->bandwidth->val = bandwidth_lut[i].freq; - dev_dbg(&s->spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq); + dev_dbg(&spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq); - f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; - tmp64 = f_vco; - m = do_div(tmp64, F_REF * R_REF); - n = (unsigned int) tmp64; + /* + * Fractional-N synthesizer + * + * +---------------------------------------+ + * v | + * Fref +----+ +-------+ +----+ +------+ +---+ + * ------> | PD | --> | VCO | ------> | /4 | --> | /N.F | <-- | K | + * +----+ +-------+ +----+ +------+ +---+ + * | + * | + * v + * +-------+ Fout + * | /Rout | ------> + * +-------+ + */ - vco_step = F_OUT_STEP * lo_div; - thresh = (F_REF * R_REF) / vco_step; - frac = 1ul * thresh * m / (F_REF * R_REF); + /* Calculate PLL integer and fractional control word. */ + f_vco = (u64) (f_rf + f_if + f_if1) * div_lo; + div_n = div_u64_rem(f_vco, DIV_PRE_N * F_REF, &k); + k_thresh = (DIV_PRE_N * F_REF) / F_VCO_STEP; + k_frac = div_u64((u64) k * k_thresh, (DIV_PRE_N * F_REF)); /* Find out greatest common divisor and divide to smaller. */ - tmp = gcd(thresh, frac); - thresh /= tmp; - frac /= tmp; + uitmp = gcd(k_thresh, k_frac); + k_thresh /= uitmp; + k_frac /= uitmp; /* Force divide to reg max. Resolution will be reduced. */ - tmp = DIV_ROUND_UP(thresh, 4095); - thresh = DIV_ROUND_CLOSEST(thresh, tmp); - frac = DIV_ROUND_CLOSEST(frac, tmp); + uitmp = DIV_ROUND_UP(k_thresh, 4095); + k_thresh = DIV_ROUND_CLOSEST(k_thresh, uitmp); + k_frac = DIV_ROUND_CLOSEST(k_frac, uitmp); - /* calc real RF set */ - tmp = 1ul * F_REF * R_REF * n; - tmp += 1ul * F_REF * R_REF * frac / thresh; - tmp /= lo_div; + /* Calculate real RF set. */ + uitmp = (unsigned int) F_REF * DIV_PRE_N * div_n; + uitmp += (unsigned int) F_REF * DIV_PRE_N * k_frac / k_thresh; + uitmp /= div_lo; - dev_dbg(&s->spi->dev, "rf=%u:%u n=%d thresh=%d frac=%d\n", - f_rf, tmp, n, thresh, frac); + dev_dbg(&spi->dev, + "f_rf=%u:%u f_vco=%llu div_n=%u k_thresh=%u k_frac=%u div_lo=%u\n", + f_rf, uitmp, f_vco, div_n, k_thresh, k_frac, div_lo); - ret = msi001_wreg(s, 0x00000e); + ret = msi001_wreg(dev, 0x00000e); if (ret) goto err; - ret = msi001_wreg(s, 0x000003); + ret = msi001_wreg(dev, 0x000003); if (ret) goto err; @@ -241,54 +254,55 @@ static int msi001_set_tuner(struct msi001 *s) reg |= bandwidth << 14; reg |= 0x02 << 17; reg |= 0x00 << 20; - ret = msi001_wreg(s, reg); + ret = msi001_wreg(dev, reg); if (ret) goto err; reg = 5 << 0; - reg |= thresh << 4; + reg |= k_thresh << 4; reg |= 1 << 19; reg |= 1 << 21; - ret = msi001_wreg(s, reg); + ret = msi001_wreg(dev, reg); if (ret) goto err; reg = 2 << 0; - reg |= frac << 4; - reg |= n << 16; - ret = msi001_wreg(s, reg); + reg |= k_frac << 4; + reg |= div_n << 16; + ret = msi001_wreg(dev, reg); if (ret) goto err; - ret = msi001_set_gain(s, s->lna_gain->cur.val, s->mixer_gain->cur.val, - s->if_gain->cur.val); + ret = msi001_set_gain(dev, dev->lna_gain->cur.val, + dev->mixer_gain->cur.val, dev->if_gain->cur.val); if (ret) goto err; reg = 6 << 0; reg |= 63 << 4; reg |= 4095 << 10; - ret = msi001_wreg(s, reg); + ret = msi001_wreg(dev, reg); if (ret) goto err; return 0; err: - dev_dbg(&s->spi->dev, "failed %d\n", ret); + dev_dbg(&spi->dev, "failed %d\n", ret); return ret; -}; +} static int msi001_s_power(struct v4l2_subdev *sd, int on) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; int ret; - dev_dbg(&s->spi->dev, "on=%d\n", on); + dev_dbg(&spi->dev, "on=%d\n", on); if (on) ret = 0; else - ret = msi001_wreg(s, 0x000000); + ret = msi001_wreg(dev, 0x000000); return ret; } @@ -299,9 +313,10 @@ static const struct v4l2_subdev_core_ops msi001_core_ops = { static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; - dev_dbg(&s->spi->dev, "index=%d\n", v->index); + dev_dbg(&spi->dev, "index=%d\n", v->index); strlcpy(v->name, "Mirics MSi001", sizeof(v->name)); v->type = V4L2_TUNER_RF; @@ -314,47 +329,51 @@ static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; - dev_dbg(&s->spi->dev, "index=%d\n", v->index); + dev_dbg(&spi->dev, "index=%d\n", v->index); return 0; } static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; - dev_dbg(&s->spi->dev, "tuner=%d\n", f->tuner); - f->frequency = s->f_tuner; + dev_dbg(&spi->dev, "tuner=%d\n", f->tuner); + f->frequency = dev->f_tuner; return 0; } static int msi001_s_frequency(struct v4l2_subdev *sd, - const struct v4l2_frequency *f) + const struct v4l2_frequency *f) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; unsigned int band; - dev_dbg(&s->spi->dev, "tuner=%d type=%d frequency=%u\n", - f->tuner, f->type, f->frequency); + dev_dbg(&spi->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2)) band = 0; else band = 1; - s->f_tuner = clamp_t(unsigned int, f->frequency, - bands[band].rangelow, bands[band].rangehigh); + dev->f_tuner = clamp_t(unsigned int, f->frequency, + bands[band].rangelow, bands[band].rangehigh); - return msi001_set_tuner(s); + return msi001_set_tuner(dev); } static int msi001_enum_freq_bands(struct v4l2_subdev *sd, - struct v4l2_frequency_band *band) + struct v4l2_frequency_band *band) { - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); + struct spi_device *spi = dev->spi; - dev_dbg(&s->spi->dev, "tuner=%d type=%d index=%d\n", - band->tuner, band->type, band->index); + dev_dbg(&spi->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->index >= ARRAY_SIZE(bands)) return -EINVAL; @@ -381,34 +400,37 @@ static const struct v4l2_subdev_ops msi001_ops = { static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) { - struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl); + struct msi001_dev *dev = container_of(ctrl->handler, struct msi001_dev, hdl); + struct spi_device *spi = dev->spi; int ret; - dev_dbg(&s->spi->dev, - "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - ctrl->id, ctrl->name, ctrl->val, - ctrl->minimum, ctrl->maximum, ctrl->step); + dev_dbg(&spi->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, + ctrl->step); switch (ctrl->id) { case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH: - ret = msi001_set_tuner(s); + ret = msi001_set_tuner(dev); break; case V4L2_CID_RF_TUNER_LNA_GAIN: - ret = msi001_set_gain(s, s->lna_gain->val, - s->mixer_gain->cur.val, s->if_gain->cur.val); + ret = msi001_set_gain(dev, dev->lna_gain->val, + dev->mixer_gain->cur.val, + dev->if_gain->cur.val); break; case V4L2_CID_RF_TUNER_MIXER_GAIN: - ret = msi001_set_gain(s, s->lna_gain->cur.val, - s->mixer_gain->val, s->if_gain->cur.val); + ret = msi001_set_gain(dev, dev->lna_gain->cur.val, + dev->mixer_gain->val, + dev->if_gain->cur.val); break; case V4L2_CID_RF_TUNER_IF_GAIN: - ret = msi001_set_gain(s, s->lna_gain->cur.val, - s->mixer_gain->cur.val, s->if_gain->val); + ret = msi001_set_gain(dev, dev->lna_gain->cur.val, + dev->mixer_gain->cur.val, + dev->if_gain->val); break; default: - dev_dbg(&s->spi->dev, "unknown control %d\n", ctrl->id); + dev_dbg(&spi->dev, "unknown control %d\n", ctrl->id); ret = -EINVAL; } @@ -421,56 +443,54 @@ static const struct v4l2_ctrl_ops msi001_ctrl_ops = { static int msi001_probe(struct spi_device *spi) { - struct msi001 *s; + struct msi001_dev *dev; int ret; dev_dbg(&spi->dev, "\n"); - s = kzalloc(sizeof(struct msi001), GFP_KERNEL); - if (s == NULL) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; - dev_dbg(&spi->dev, "Could not allocate memory for msi001\n"); - goto err_kfree; + goto err; } - s->spi = spi; - s->f_tuner = bands[0].rangelow; - v4l2_spi_subdev_init(&s->sd, spi, &msi001_ops); + dev->spi = spi; + dev->f_tuner = bands[0].rangelow; + v4l2_spi_subdev_init(&dev->sd, spi, &msi001_ops); /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 5); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + v4l2_ctrl_handler_init(&dev->hdl, 5); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops, V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); - s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); + dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops, V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1); - s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + dev->mixer_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops, V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); - s->if_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops, V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->spi->dev, "Could not initialize controls\n"); + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(&spi->dev, "Could not initialize controls\n"); /* control init failed, free handler */ goto err_ctrl_handler_free; } - s->sd.ctrl_handler = &s->hdl; + dev->sd.ctrl_handler = &dev->hdl; return 0; - err_ctrl_handler_free: - v4l2_ctrl_handler_free(&s->hdl); -err_kfree: - kfree(s); + v4l2_ctrl_handler_free(&dev->hdl); + kfree(dev); +err: return ret; } static int msi001_remove(struct spi_device *spi) { struct v4l2_subdev *sd = spi_get_drvdata(spi); - struct msi001 *s = sd_to_msi001(sd); + struct msi001_dev *dev = sd_to_msi001_dev(sd); dev_dbg(&spi->dev, "\n"); @@ -478,26 +498,26 @@ static int msi001_remove(struct spi_device *spi) * Registered by v4l2_spi_new_subdev() from master driver, but we must * unregister it from here. Weird. */ - v4l2_device_unregister_subdev(&s->sd); - v4l2_ctrl_handler_free(&s->hdl); - kfree(s); + v4l2_device_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->hdl); + kfree(dev); return 0; } -static const struct spi_device_id msi001_id[] = { +static const struct spi_device_id msi001_id_table[] = { {"msi001", 0}, {} }; -MODULE_DEVICE_TABLE(spi, msi001_id); +MODULE_DEVICE_TABLE(spi, msi001_id_table); static struct spi_driver msi001_driver = { .driver = { .name = "msi001", - .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, .probe = msi001_probe, .remove = msi001_remove, - .id_table = msi001_id, + .id_table = msi001_id_table, }; module_spi_driver(msi001_driver); diff --git a/kernel/drivers/media/tuners/qt1010.c b/kernel/drivers/media/tuners/qt1010.c index bc419f8a9..ae8cbece6 100644 --- a/kernel/drivers/media/tuners/qt1010.c +++ b/kernel/drivers/media/tuners/qt1010.c @@ -294,7 +294,7 @@ static int qt1010_init(struct dvb_frontend *fe) int err = 0; u8 i, tmpval, *valptr = NULL; - qt1010_i2c_oper_t i2c_data[] = { + static const qt1010_i2c_oper_t i2c_data[] = { { QT1010_WR, 0x01, 0x80 }, { QT1010_WR, 0x0d, 0x84 }, { QT1010_WR, 0x0e, 0xb7 }, @@ -354,13 +354,17 @@ static int qt1010_init(struct dvb_frontend *fe) valptr = &priv->reg1f_init_val; else valptr = &tmpval; + + BUG_ON(i >= ARRAY_SIZE(i2c_data) - 1); + err = qt1010_init_meas1(priv, i2c_data[i+1].reg, i2c_data[i].reg, i2c_data[i].val, valptr); i++; break; } - if (err) return err; + if (err) + return err; } for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ diff --git a/kernel/drivers/media/tuners/r820t.c b/kernel/drivers/media/tuners/r820t.c index 71159a588..a7a8452e9 100644 --- a/kernel/drivers/media/tuners/r820t.c +++ b/kernel/drivers/media/tuners/r820t.c @@ -941,8 +941,8 @@ static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x04); if (rc < 0) return rc; - } - return 0; + } + return 0; } static int r820t_set_tv_standard(struct r820t_priv *priv, diff --git a/kernel/drivers/media/tuners/si2157.c b/kernel/drivers/media/tuners/si2157.c index d74ae2662..0e1ca2b00 100644 --- a/kernel/drivers/media/tuners/si2157.c +++ b/kernel/drivers/media/tuners/si2157.c @@ -79,6 +79,7 @@ static int si2157_init(struct dvb_frontend *fe) { struct i2c_client *client = fe->tuner_priv; struct si2157_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, len, remaining; struct si2157_cmd cmd; const struct firmware *fw; @@ -165,6 +166,11 @@ static int si2157_init(struct dvb_frontend *fe) for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; + if (len > SI2157_ARGLEN) { + dev_err(&client->dev, "Bad firmware length\n"); + ret = -EINVAL; + goto err_release_firmware; + } memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; @@ -201,9 +207,14 @@ skip_fw_download: dev->fw_loaded = true; warm: + /* init statistics in order signal app which are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* start statistics polling */ + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(1000)); + dev->active = true; return 0; - err_release_firmware: release_firmware(fw); err: @@ -222,6 +233,9 @@ static int si2157_sleep(struct dvb_frontend *fe) dev->active = false; + /* stop statistics polling */ + cancel_delayed_work_sync(&dev->stat_work); + /* standby */ memcpy(cmd.args, "\x16\x00", 2); cmd.wlen = 2; @@ -298,7 +312,8 @@ static int si2157_set_params(struct dvb_frontend *fe) if (dev->chiptype == SI2157_CHIPTYPE_SI2146) memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6); else - memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6); + memcpy(cmd.args, "\x14\x00\x02\x07\x00\x00", 6); + cmd.args[4] = dev->if_port; cmd.wlen = 6; cmd.rlen = 4; ret = si2157_cmd_execute(client, &cmd); @@ -359,6 +374,34 @@ static const struct dvb_tuner_ops si2157_ops = { .get_if_frequency = si2157_get_if_frequency, }; +static void si2157_stat_work(struct work_struct *work) +{ + struct si2157_dev *dev = container_of(work, struct si2157_dev, stat_work.work); + struct dvb_frontend *fe = dev->fe; + struct i2c_client *client = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct si2157_cmd cmd; + int ret; + + dev_dbg(&client->dev, "\n"); + + memcpy(cmd.args, "\x42\x00", 2); + cmd.wlen = 2; + cmd.rlen = 12; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = (s8) cmd.args[3] * 1000; + + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + return; +err: + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + dev_dbg(&client->dev, "failed=%d\n", ret); +} + static int si2157_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -378,10 +421,12 @@ static int si2157_probe(struct i2c_client *client, i2c_set_clientdata(client, dev); dev->fe = cfg->fe; dev->inversion = cfg->inversion; + dev->if_port = cfg->if_port; dev->fw_loaded = false; dev->chiptype = (u8)id->driver_data; dev->if_frequency = 5000000; /* default value of property 0x0706 */ mutex_init(&dev->i2c_mutex); + INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work); /* check if the tuner is there */ cmd.wlen = 0; @@ -429,7 +474,6 @@ MODULE_DEVICE_TABLE(i2c, si2157_id_table); static struct i2c_driver si2157_driver = { .driver = { - .owner = THIS_MODULE, .name = "si2157", }, .probe = si2157_probe, diff --git a/kernel/drivers/media/tuners/si2157.h b/kernel/drivers/media/tuners/si2157.h index a564c4a9f..4db97ab74 100644 --- a/kernel/drivers/media/tuners/si2157.h +++ b/kernel/drivers/media/tuners/si2157.h @@ -34,6 +34,12 @@ struct si2157_config { * Spectral Inversion */ bool inversion; + + /* + * Port selection + * Select the RF interface to use (pins 9+11 or 12+13) + */ + u8 if_port; }; #endif diff --git a/kernel/drivers/media/tuners/si2157_priv.h b/kernel/drivers/media/tuners/si2157_priv.h index cd8fa5b25..ecc463db8 100644 --- a/kernel/drivers/media/tuners/si2157_priv.h +++ b/kernel/drivers/media/tuners/si2157_priv.h @@ -28,7 +28,9 @@ struct si2157_dev { bool fw_loaded; bool inversion; u8 chiptype; + u8 if_port; u32 if_frequency; + struct delayed_work stat_work; }; #define SI2157_CHIPTYPE_SI2157 0 diff --git a/kernel/drivers/media/tuners/tda18212.c b/kernel/drivers/media/tuners/tda18212.c index d93e0667b..7b8068354 100644 --- a/kernel/drivers/media/tuners/tda18212.c +++ b/kernel/drivers/media/tuners/tda18212.c @@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(i2c, tda18212_id); static struct i2c_driver tda18212_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda18212", }, .probe = tda18212_probe, diff --git a/kernel/drivers/media/tuners/tua9001.c b/kernel/drivers/media/tuners/tua9001.c index 83a6240f6..9d70378fe 100644 --- a/kernel/drivers/media/tuners/tua9001.c +++ b/kernel/drivers/media/tuners/tua9001.c @@ -1,5 +1,5 @@ /* - * Infineon TUA 9001 silicon tuner driver + * Infineon TUA9001 silicon tuner driver * * Copyright (C) 2009 Antti Palosaari * @@ -12,138 +12,87 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "tua9001.h" #include "tua9001_priv.h" -/* write register */ -static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) -{ - int ret; - u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_addr, - .flags = 0, - .len = sizeof(buf), - .buf = buf, - } - }; - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n", - KBUILD_MODNAME, ret, reg); - ret = -EREMOTEIO; - } - - return ret; -} - -static int tua9001_release(struct dvb_frontend *fe) -{ - struct tua9001_priv *priv = fe->tuner_priv; - int ret = 0; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - - if (fe->callback) - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_CEN, 0); - - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - - return ret; -} - static int tua9001_init(struct dvb_frontend *fe) { - struct tua9001_priv *priv = fe->tuner_priv; - int ret = 0; - u8 i; - struct reg_val data[] = { - { 0x1e, 0x6512 }, - { 0x25, 0xb888 }, - { 0x39, 0x5460 }, - { 0x3b, 0x00c0 }, - { 0x3a, 0xf000 }, - { 0x08, 0x0000 }, - { 0x32, 0x0030 }, - { 0x41, 0x703a }, - { 0x40, 0x1c78 }, - { 0x2c, 0x1c00 }, - { 0x36, 0xc013 }, - { 0x37, 0x6f18 }, - { 0x27, 0x0008 }, - { 0x2a, 0x0001 }, - { 0x34, 0x0a40 }, + struct tua9001_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; + int ret, i; + static const struct tua9001_reg_val data[] = { + {0x1e, 0x6512}, + {0x25, 0xb888}, + {0x39, 0x5460}, + {0x3b, 0x00c0}, + {0x3a, 0xf000}, + {0x08, 0x0000}, + {0x32, 0x0030}, + {0x41, 0x703a}, + {0x40, 0x1c78}, + {0x2c, 0x1c00}, + {0x36, 0xc013}, + {0x37, 0x6f18}, + {0x27, 0x0008}, + {0x2a, 0x0001}, + {0x34, 0x0a40}, }; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RESETN, 0); - if (ret < 0) + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RESETN, 0); + if (ret) goto err; } - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ - for (i = 0; i < ARRAY_SIZE(data); i++) { - ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); - if (ret < 0) - goto err_i2c_gate_ctrl; + ret = regmap_write(dev->regmap, data[i].reg, data[i].val); + if (ret) + goto err; } - -err_i2c_gate_ctrl: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + return 0; err: - if (ret < 0) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tua9001_sleep(struct dvb_frontend *fe) { - struct tua9001_priv *priv = fe->tuner_priv; - int ret = 0; - - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - - if (fe->callback) - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RESETN, 1); + struct tua9001_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; + int ret; - if (ret < 0) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "\n"); + if (fe->callback) { + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RESETN, 1); + if (ret) + goto err; + } + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tua9001_set_params(struct dvb_frontend *fe) { - struct tua9001_priv *priv = fe->tuner_priv; + struct tua9001_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret = 0, i; + int ret, i; u16 val; - u32 frequency; - struct reg_val data[2]; + struct tua9001_reg_val data[2]; - dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ - "bandwidth_hz=%d\n", __func__, - c->delivery_system, c->frequency, c->bandwidth_hz); + dev_dbg(&client->dev, + "delivery_system=%u frequency=%u bandwidth_hz=%u\n", + c->delivery_system, c->frequency, c->bandwidth_hz); switch (c->delivery_system) { case SYS_DVBT: @@ -172,70 +121,54 @@ static int tua9001_set_params(struct dvb_frontend *fe) data[0].reg = 0x04; data[0].val = val; - - frequency = (c->frequency - 150000000); - frequency /= 100; - frequency *= 48; - frequency /= 10000; - data[1].reg = 0x1f; - data[1].val = frequency; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + data[1].val = div_u64((u64) (c->frequency - 150000000) * 48, 1000000); if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RXEN, 0); - if (ret < 0) - goto err_i2c_gate_ctrl; + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RXEN, 0); + if (ret) + goto err; } for (i = 0; i < ARRAY_SIZE(data); i++) { - ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); - if (ret < 0) - goto err_i2c_gate_ctrl; + ret = regmap_write(dev->regmap, data[i].reg, data[i].val); + if (ret) + goto err; } if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RXEN, 1); - if (ret < 0) - goto err_i2c_gate_ctrl; + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RXEN, 1); + if (ret) + goto err; } - -err_i2c_gate_ctrl: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + return 0; err: - if (ret < 0) - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct tua9001_priv *priv = fe->tuner_priv; + struct tua9001_dev *dev = fe->tuner_priv; + struct i2c_client *client = dev->client; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); *frequency = 0; /* Zero-IF */ - return 0; } static const struct dvb_tuner_ops tua9001_tuner_ops = { .info = { - .name = "Infineon TUA 9001", - + .name = "Infineon TUA9001", .frequency_min = 170000000, .frequency_max = 862000000, - .frequency_step = 0, }, - .release = tua9001_release, - .init = tua9001_init, .sleep = tua9001_sleep, .set_params = tua9001_set_params, @@ -243,52 +176,107 @@ static const struct dvb_tuner_ops tua9001_tuner_ops = { .get_if_frequency = tua9001_get_if_frequency, }; -struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tua9001_config *cfg) +static int tua9001_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct tua9001_priv *priv = NULL; + struct tua9001_dev *dev; + struct tua9001_platform_data *pdata = client->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; int ret; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 16, + }; - priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } - priv->cfg = cfg; - priv->i2c = i2c; + dev->fe = pdata->dvb_frontend; + dev->client = client; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_CEN, 1); - if (ret < 0) - goto err; - - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RXEN, 0); - if (ret < 0) - goto err; - - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - TUA9001_CMD_RESETN, 1); - if (ret < 0) - goto err; + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_CEN, 1); + if (ret) + goto err_kfree; + + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RXEN, 0); + if (ret) + goto err_kfree; + + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_RESETN, 1); + if (ret) + goto err_kfree; } - dev_info(&priv->i2c->dev, - "%s: Infineon TUA 9001 successfully attached\n", - KBUILD_MODNAME); - + fe->tuner_priv = dev; memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, sizeof(struct dvb_tuner_ops)); + i2c_set_clientdata(client, dev); - fe->tuner_priv = priv; - return fe; + dev_info(&client->dev, "Infineon TUA9001 successfully attached\n"); + return 0; +err_kfree: + kfree(dev); err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int tua9001_remove(struct i2c_client *client) +{ + struct tua9001_dev *dev = i2c_get_clientdata(client); + struct dvb_frontend *fe = dev->fe; + int ret; + + dev_dbg(&client->dev, "\n"); + + if (fe->callback) { + ret = fe->callback(client->adapter, + DVB_FRONTEND_COMPONENT_TUNER, + TUA9001_CMD_CEN, 0); + if (ret) + goto err_kfree; + } + kfree(dev); + return 0; +err_kfree: + kfree(dev); + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(tua9001_attach); -MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); +static const struct i2c_device_id tua9001_id_table[] = { + {"tua9001", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tua9001_id_table); + +static struct i2c_driver tua9001_driver = { + .driver = { + .name = "tua9001", + .suppress_bind_attrs = true, + }, + .probe = tua9001_probe, + .remove = tua9001_remove, + .id_table = tua9001_id_table, +}; + +module_i2c_driver(tua9001_driver); + +MODULE_DESCRIPTION("Infineon TUA9001 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/tuners/tua9001.h b/kernel/drivers/media/tuners/tua9001.h index 2c3375c7a..7b0548181 100644 --- a/kernel/drivers/media/tuners/tua9001.h +++ b/kernel/drivers/media/tuners/tua9001.h @@ -1,5 +1,5 @@ /* - * Infineon TUA 9001 silicon tuner driver + * Infineon TUA9001 silicon tuner driver * * Copyright (C) 2009 Antti Palosaari * @@ -12,23 +12,24 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TUA9001_H #define TUA9001_H -#include #include "dvb_frontend.h" -struct tua9001_config { - /* - * I2C address - */ - u8 i2c_addr; +/* + * I2C address + * 0x60, + */ + +/** + * struct tua9001_platform_data - Platform data for the tua9001 driver + * @dvb_frontend: DVB frontend. + */ +struct tua9001_platform_data { + struct dvb_frontend *dvb_frontend; }; /* @@ -51,16 +52,4 @@ struct tua9001_config { #define TUA9001_CMD_RESETN 1 #define TUA9001_CMD_RXEN 2 -#if IS_REACHABLE(CONFIG_MEDIA_TUNER_TUA9001) -extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tua9001_config *cfg); -#else -static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tua9001_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif - #endif diff --git a/kernel/drivers/media/tuners/tua9001_priv.h b/kernel/drivers/media/tuners/tua9001_priv.h index 73cc1ce05..bc406c5ec 100644 --- a/kernel/drivers/media/tuners/tua9001_priv.h +++ b/kernel/drivers/media/tuners/tua9001_priv.h @@ -1,5 +1,5 @@ /* - * Infineon TUA 9001 silicon tuner driver + * Infineon TUA9001 silicon tuner driver * * Copyright (C) 2009 Antti Palosaari * @@ -12,23 +12,24 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TUA9001_PRIV_H #define TUA9001_PRIV_H -struct reg_val { +#include "tua9001.h" +#include +#include + +struct tua9001_reg_val { u8 reg; u16 val; }; -struct tua9001_priv { - struct tua9001_config *cfg; - struct i2c_adapter *i2c; +struct tua9001_dev { + struct dvb_frontend *fe; + struct i2c_client *client; + struct regmap *regmap; }; #endif diff --git a/kernel/drivers/media/tuners/tuner-i2c.h b/kernel/drivers/media/tuners/tuner-i2c.h index 18f005634..bda67a5a7 100644 --- a/kernel/drivers/media/tuners/tuner-i2c.h +++ b/kernel/drivers/media/tuners/tuner-i2c.h @@ -33,7 +33,8 @@ struct tuner_i2c_props { char *name; }; -static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len) +static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, + unsigned char *buf, int len) { struct i2c_msg msg = { .addr = props->addr, .flags = 0, .buf = buf, .len = len }; @@ -42,7 +43,8 @@ static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, return (ret == 1) ? len : ret; } -static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, int len) +static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, + unsigned char *buf, int len) { struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD, .buf = buf, .len = len }; @@ -52,8 +54,8 @@ static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, } static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, - char *obuf, int olen, - char *ibuf, int ilen) + unsigned char *obuf, int olen, + unsigned char *ibuf, int ilen) { struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, .buf = obuf, .len = olen }, diff --git a/kernel/drivers/media/tuners/tuner-xc2028.c b/kernel/drivers/media/tuners/tuner-xc2028.c index d12f5e4ad..4e941f00b 100644 --- a/kernel/drivers/media/tuners/tuner-xc2028.c +++ b/kernel/drivers/media/tuners/tuner-xc2028.c @@ -1094,7 +1094,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, * Still need tests for XC3028L (firmware 3.2 or upper) * So, for now, let's just comment the per-firmware * version of this change. Reports with xc3028l working - * with and without the lines bellow are welcome + * with and without the lines below are welcome */ if (priv->firm_version < 0x0302) { diff --git a/kernel/drivers/media/usb/airspy/airspy.c b/kernel/drivers/media/usb/airspy/airspy.c index 4069234ab..565a59310 100644 --- a/kernel/drivers/media/usb/airspy/airspy.c +++ b/kernel/drivers/media/usb/airspy/airspy.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* AirSpy USB API commands (from AirSpy Library) */ @@ -97,7 +98,8 @@ static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); /* intermediate buffers with raw data from the USB device */ struct airspy_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -132,7 +134,7 @@ struct airspy { int urbs_submitted; /* USB control message buffer */ - #define BUF_SIZE 24 + #define BUF_SIZE 128 u8 buf[BUF_SIZE]; /* Current configuration */ @@ -310,13 +312,13 @@ static void airspy_urb_complete(struct urb *urb) } /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); + ptr = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0); len = airspy_convert_stream(s, ptr, urb->transfer_buffer, urb->actual_length); - vb2_set_plane_payload(&fbuf->vb, 0, len); - v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = s->sequence++; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len); + v4l2_get_timestamp(&fbuf->vb.timestamp); + fbuf->vb.sequence = s->sequence++; + vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); } skip: usb_submit_urb(urb, GFP_ATOMIC); @@ -459,7 +461,7 @@ static void airspy_cleanup_queued_bufs(struct airspy *s) buf = list_entry(s->queued_bufs.next, struct airspy_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&s->queued_bufs_lock, flags); } @@ -486,7 +488,7 @@ static void airspy_disconnect(struct usb_interface *intf) /* Videobuf2 operations */ static int airspy_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct airspy *s = vb2_get_drv_priv(vq); @@ -505,14 +507,15 @@ static int airspy_queue_setup(struct vb2_queue *vq, static void airspy_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct airspy *s = vb2_get_drv_priv(vb->vb2_queue); struct airspy_frame_buf *buf = - container_of(vb, struct airspy_frame_buf, vb); + container_of(vbuf, struct airspy_frame_buf, vb); unsigned long flags; /* Check the device has not disconnected between prep and queuing */ if (unlikely(!s->udev)) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); return; } @@ -571,7 +574,8 @@ err_clear_bit: list_for_each_entry_safe(buf, tmp, &s->queued_bufs, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); } } @@ -936,9 +940,6 @@ static int airspy_set_if_gain(struct airspy *s) ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, &u8tmp, 1); - if (ret) - goto err; -err: if (ret) dev_dbg(s->dev, "failed=%d\n", ret); diff --git a/kernel/drivers/media/usb/as102/as102_drv.c b/kernel/drivers/media/usb/as102/as102_drv.c index 8be1474b2..9dd7c7cb0 100644 --- a/kernel/drivers/media/usb/as102/as102_drv.c +++ b/kernel/drivers/media/usb/as102/as102_drv.c @@ -337,6 +337,7 @@ int as102_dvb_register(struct as102_dev_t *as102_dev) &as102_dev->bus_adap, as102_dev->elna_cfg); if (!as102_dev->dvb_fe) { + ret = -ENODEV; dev_err(dev, "%s: as102_attach() failed: %d", __func__, ret); goto efereg; diff --git a/kernel/drivers/media/usb/au0828/au0828-cards.c b/kernel/drivers/media/usb/au0828/au0828-cards.c index edc27355f..6b469e8c4 100644 --- a/kernel/drivers/media/usb/au0828/au0828-cards.c +++ b/kernel/drivers/media/usb/au0828/au0828-cards.c @@ -195,8 +195,6 @@ void au0828_card_setup(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); - dev->board = au0828_boards[dev->boardnr]; - if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); diff --git a/kernel/drivers/media/usb/au0828/au0828-core.c b/kernel/drivers/media/usb/au0828/au0828-core.c index 082ae6ba4..0934024fb 100644 --- a/kernel/drivers/media/usb/au0828/au0828-core.c +++ b/kernel/drivers/media/usb/au0828/au0828-core.c @@ -222,6 +222,8 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_init(&dev->dvb.lock); dev->usbdev = usbdev; dev->boardnr = id->driver_info; + dev->board = au0828_boards[dev->boardnr]; + #ifdef CONFIG_VIDEO_AU0828_V4L2 dev->v4l2_dev.release = au0828_usb_v4l2_release; diff --git a/kernel/drivers/media/usb/au0828/au0828-vbi.c b/kernel/drivers/media/usb/au0828/au0828-vbi.c index f67247cf1..130c8b49b 100644 --- a/kernel/drivers/media/usb/au0828/au0828-vbi.c +++ b/kernel/drivers/media/usb/au0828/au0828-vbi.c @@ -30,10 +30,11 @@ /* ------------------------------------------------------------------ */ -static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vbi_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct au0828_dev *dev = vb2_get_drv_priv(vq); unsigned long img_size = dev->vbi_width * dev->vbi_height * 2; unsigned long size; @@ -52,7 +53,6 @@ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int vbi_buffer_prepare(struct vb2_buffer *vb) { struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); unsigned long size; size = dev->vbi_width * dev->vbi_height * 2; @@ -62,7 +62,7 @@ static int vbi_buffer_prepare(struct vb2_buffer *vb) __func__, vb2_plane_size(vb, 0), size); return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(vb, 0, size); return 0; } @@ -71,7 +71,9 @@ static void vbi_buffer_queue(struct vb2_buffer *vb) { struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct au0828_buffer *buf = + container_of(vbuf, struct au0828_buffer, vb); struct au0828_dmaqueue *vbiq = &dev->vbiq; unsigned long flags = 0; diff --git a/kernel/drivers/media/usb/au0828/au0828-video.c b/kernel/drivers/media/usb/au0828/au0828-video.c index 1a362a041..45c622e23 100644 --- a/kernel/drivers/media/usb/au0828/au0828-video.c +++ b/kernel/drivers/media/usb/au0828/au0828-video.c @@ -302,20 +302,20 @@ static inline void buffer_filled(struct au0828_dev *dev, struct au0828_dmaqueue *dma_q, struct au0828_buffer *buf) { - struct vb2_buffer *vb = &buf->vb; - struct vb2_queue *q = vb->vb2_queue; + struct vb2_v4l2_buffer *vb = &buf->vb; + struct vb2_queue *q = vb->vb2_buf.vb2_queue; /* Advice that buffer was filled */ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - vb->v4l2_buf.sequence = dev->frame_count++; + vb->sequence = dev->frame_count++; else - vb->v4l2_buf.sequence = dev->vbi_frame_count++; + vb->sequence = dev->vbi_frame_count++; - vb->v4l2_buf.field = V4L2_FIELD_INTERLACED; - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + vb->field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&vb->timestamp); + vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); } /* @@ -531,11 +531,11 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) buf = dev->isoc_ctl.buf; if (buf != NULL) - outp = vb2_plane_vaddr(&buf->vb, 0); + outp = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); vbi_buf = dev->isoc_ctl.vbi_buf; if (vbi_buf != NULL) - vbioutp = vb2_plane_vaddr(&vbi_buf->vb, 0); + vbioutp = vb2_plane_vaddr(&vbi_buf->vb.vb2_buf, 0); for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -574,7 +574,7 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) vbioutp = NULL; else vbioutp = vb2_plane_vaddr( - &vbi_buf->vb, 0); + &vbi_buf->vb.vb2_buf, 0); /* Video */ if (buf != NULL) @@ -583,7 +583,8 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (buf == NULL) outp = NULL; else - outp = vb2_plane_vaddr(&buf->vb, 0); + outp = vb2_plane_vaddr( + &buf->vb.vb2_buf, 0); /* As long as isoc traffic is arriving, keep resetting the timer */ @@ -637,10 +638,11 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) return rc; } -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct au0828_dev *dev = vb2_get_drv_priv(vq); unsigned long img_size = dev->height * dev->bytesperline; unsigned long size; @@ -658,7 +660,9 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { - struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct au0828_buffer *buf = container_of(vbuf, + struct au0828_buffer, vb); struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); buf->length = dev->height * dev->bytesperline; @@ -668,14 +672,15 @@ buffer_prepare(struct vb2_buffer *vb) __func__, vb2_plane_size(vb, 0), buf->length); return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, buf->length); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->length); return 0; } static void buffer_queue(struct vb2_buffer *vb) { - struct au0828_buffer *buf = container_of(vb, + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct au0828_buffer *buf = container_of(vbuf, struct au0828_buffer, vb); struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); @@ -826,14 +831,15 @@ static void au0828_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&dev->slock, flags); if (dev->isoc_ctl.buf != NULL) { - vb2_buffer_done(&dev->isoc_ctl.buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&dev->isoc_ctl.buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); dev->isoc_ctl.buf = NULL; } while (!list_empty(&vidq->active)) { struct au0828_buffer *buf; buf = list_entry(vidq->active.next, struct au0828_buffer, list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); list_del(&buf->list); } spin_unlock_irqrestore(&dev->slock, flags); @@ -853,7 +859,7 @@ void au0828_stop_vbi_streaming(struct vb2_queue *vq) spin_lock_irqsave(&dev->slock, flags); if (dev->isoc_ctl.vbi_buf != NULL) { - vb2_buffer_done(&dev->isoc_ctl.vbi_buf->vb, + vb2_buffer_done(&dev->isoc_ctl.vbi_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dev->isoc_ctl.vbi_buf = NULL; } @@ -862,7 +868,7 @@ void au0828_stop_vbi_streaming(struct vb2_queue *vq) buf = list_entry(vbiq->active.next, struct au0828_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); @@ -911,7 +917,7 @@ static void au0828_vid_buffer_timeout(unsigned long data) buf = dev->isoc_ctl.buf; if (buf != NULL) { - vid_data = vb2_plane_vaddr(&buf->vb, 0); + vid_data = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); memset(vid_data, 0x00, buf->length); /* Blank green frame */ buffer_filled(dev, dma_q, buf); } @@ -935,7 +941,7 @@ static void au0828_vbi_buffer_timeout(unsigned long data) buf = dev->isoc_ctl.vbi_buf; if (buf != NULL) { - vbi_data = vb2_plane_vaddr(&buf->vb, 0); + vbi_data = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); memset(vbi_data, 0x00, buf->length); buffer_filled(dev, dma_q, buf); } diff --git a/kernel/drivers/media/usb/au0828/au0828.h b/kernel/drivers/media/usb/au0828/au0828.h index 3b480005c..60b59391e 100644 --- a/kernel/drivers/media/usb/au0828/au0828.h +++ b/kernel/drivers/media/usb/au0828/au0828.h @@ -28,6 +28,7 @@ /* Analog */ #include +#include #include #include #include @@ -167,7 +168,7 @@ struct au0828_usb_isoc_ctl { /* buffer for one video frame */ struct au0828_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; void *mem; diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-417.c b/kernel/drivers/media/usb/cx231xx/cx231xx-417.c index 983ea8339..47a98a201 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1160,9 +1160,9 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) } cx231xx_enable656(dev); - /* stop mpeg capture */ - cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, - 3, 0, 1, 3, 4); + + /* stop mpeg capture */ + cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, 1, 3, 4); cx231xx_codec_settings(dev); msleep(60); @@ -1249,8 +1249,7 @@ static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) struct cx231xx *dev = fh->dev; unsigned long flags = 0; - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); spin_lock_irqsave(&dev->video_mode.slock, flags); if (dev->USE_ISO) { @@ -1878,13 +1877,15 @@ static int cx231xx_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) { struct cx231xx *dev = container_of(cxhdl, struct cx231xx, mpeg_ctrl_handler); int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; /* fix videodecoder resolution */ - fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); - fmt.height = cxhdl->height; - fmt.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(dev->sd_cx25840, video, s_mbus_fmt, &fmt); + format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1); + format.format.height = cxhdl->height; + format.format.code = MEDIA_BUS_FMT_FIXED; + v4l2_subdev_call(dev->sd_cx25840, pad, set_fmt, NULL, &format); return 0; } diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-avcore.c b/kernel/drivers/media/usb/cx231xx/cx231xx-avcore.c index 39e887925..491913778 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -1595,31 +1595,31 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, /*pll_freq_word = 0x3463497;*/ vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); - if (spectral_invert) { - if_freq -= 400000; - /* Enable Spectral Invert*/ - vid_blk_read_word(dev, DIF_MISC_CTRL, - &dif_misc_ctrl_value); - dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; - vid_blk_write_word(dev, DIF_MISC_CTRL, - dif_misc_ctrl_value); - } else { - if_freq += 400000; - /* Disable Spectral Invert*/ - vid_blk_read_word(dev, DIF_MISC_CTRL, - &dif_misc_ctrl_value); - dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; - vid_blk_write_word(dev, DIF_MISC_CTRL, - dif_misc_ctrl_value); - } + if (spectral_invert) { + if_freq -= 400000; + /* Enable Spectral Invert*/ + vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; + vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } else { + if_freq += 400000; + /* Disable Spectral Invert*/ + vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; + vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } - if_freq = (if_freq/100000)*100000; + if_freq = (if_freq / 100000) * 100000; - if (if_freq < 3000000) - if_freq = 3000000; + if (if_freq < 3000000) + if_freq = 3000000; - if (if_freq > 16000000) - if_freq = 16000000; + if (if_freq > 16000000) + if_freq = 16000000; } dev_dbg(dev->dev, "Enter IF=%zu\n", ARRAY_SIZE(Dif_set_array)); diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-cards.c b/kernel/drivers/media/usb/cx231xx/cx231xx-cards.c index fe00da105..4a117a58c 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -815,6 +815,32 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_TERRATEC_GRABBY] = { + .name = "Terratec Grabby", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_PAL, + .no_alt_vanc = 1, + .external_av = 1, + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -880,6 +906,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2}, {USB_DEVICE(0x1f4d, 0x0102), .driver_info = CX231XX_BOARD_OTG102}, + {USB_DEVICE(USB_VID_TERRATEC, 0x00a6), + .driver_info = CX231XX_BOARD_TERRATEC_GRABBY}, {}, }; @@ -1092,17 +1120,25 @@ void cx231xx_card_setup(struct cx231xx *dev) case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: case CX231XX_BOARD_HAUPPAUGE_955Q: { - struct tveeprom tvee; - static u8 eeprom[256]; - struct i2c_client client; - - memset(&client, 0, sizeof(client)); - client.adapter = cx231xx_get_i2c_adap(dev, I2C_1_MUX_1); - client.addr = 0xa0 >> 1; + struct eeprom { + struct tveeprom tvee; + u8 eeprom[256]; + struct i2c_client client; + }; + struct eeprom *e = kzalloc(sizeof(*e), GFP_KERNEL); + + if (e == NULL) { + dev_err(dev->dev, + "failed to allocate memory to read eeprom\n"); + break; + } + e->client.adapter = cx231xx_get_i2c_adap(dev, I2C_1_MUX_1); + e->client.addr = 0xa0 >> 1; - read_eeprom(dev, &client, eeprom, sizeof(eeprom)); - tveeprom_hauppauge_analog(&client, - &tvee, eeprom + 0xc0); + read_eeprom(dev, &e->client, e->eeprom, sizeof(e->eeprom)); + tveeprom_hauppauge_analog(&e->client, + &e->tvee, e->eeprom + 0xc0); + kfree(e); break; } } diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-core.c b/kernel/drivers/media/usb/cx231xx/cx231xx-core.c index e42bde081..a2fd49b6b 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-core.c @@ -653,22 +653,20 @@ int cx231xx_demod_reset(struct cx231xx *dev) cx231xx_coredbg("Enter cx231xx_demod_reset()\n"); - value[1] = (u8) 0x3; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - value[1] = (u8) 0x0; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - value[1] = (u8) 0x3; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - PWR_CTL_EN, value, 4); - msleep(10); - - + value[1] = (u8) 0x3; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); + + value[1] = (u8) 0x0; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); + + value[1] = (u8) 0x3; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(10); status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, 4); diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-dvb.c b/kernel/drivers/media/usb/cx231xx/cx231xx-dvb.c index 610d5675b..66ee161fc 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -797,6 +797,7 @@ static int dvb_init(struct cx231xx *dev) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dev->dvb->frontend; + si2157_config.if_port = 1; si2157_config.inversion = true; strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -852,6 +853,7 @@ static int dvb_init(struct cx231xx *dev) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dev->dvb->frontend; + si2157_config.if_port = 1; si2157_config.inversion = true; strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-vbi.c b/kernel/drivers/media/usb/cx231xx/cx231xx-vbi.c index 80261ac40..a08014d20 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -192,8 +192,7 @@ static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) struct cx231xx_fh *fh = vq->priv_data; struct cx231xx *dev = fh->dev; unsigned long flags = 0; - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); /* We used to wait for the buffer to finish here, but this didn't work because, as we were keeping the state as VIDEOBUF_QUEUED, diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx-video.c b/kernel/drivers/media/usb/cx231xx/cx231xx-video.c index c261e160c..d0d8f08e3 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/kernel/drivers/media/usb/cx231xx/cx231xx-video.c @@ -749,8 +749,7 @@ static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) struct cx231xx *dev = fh->dev; unsigned long flags = 0; - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); /* We used to wait for the buffer to finish here, but this didn't work because, as we were keeping the state as VIDEOBUF_QUEUED, @@ -1013,7 +1012,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct cx231xx *dev = fh->dev; int rc; struct cx231xx_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; rc = check_dev(dev); if (rc < 0) @@ -1041,9 +1042,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, dev->height = f->fmt.pix.height; dev->format = fmt; - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - call_all(dev, video, s_mbus_fmt, &mbus_fmt); - v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); + call_all(dev, pad, set_fmt, NULL, &format); + v4l2_fill_pix_format(&f->fmt.pix, &format.format); return rc; } @@ -1061,7 +1062,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int rc; rc = check_dev(dev); @@ -1085,11 +1088,10 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) /* We need to reset basic properties in the decoder related to resolution (since a standard change effects things like the number of lines in VACT, etc) */ - memset(&mbus_fmt, 0, sizeof(mbus_fmt)); - mbus_fmt.code = MEDIA_BUS_FMT_FIXED; - mbus_fmt.width = dev->width; - mbus_fmt.height = dev->height; - call_all(dev, video, s_mbus_fmt, &mbus_fmt); + format.format.code = MEDIA_BUS_FMT_FIXED; + format.format.width = dev->width; + format.format.height = dev->height; + call_all(dev, pad, set_fmt, NULL, &format); /* do mode control overrides */ cx231xx_do_mode_ctrl_overrides(dev); @@ -1112,7 +1114,8 @@ int cx231xx_enum_input(struct file *file, void *priv, struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; u32 gen_stat; - unsigned int ret, n; + unsigned int n; + int ret; n = i->index; if (n >= MAX_CX231XX_INPUT) @@ -1873,7 +1876,7 @@ static int cx231xx_close(struct file *filp) v4l2_fh_exit(&fh->fh); kfree(fh); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); + wake_up_interruptible(&dev->open); return 0; } @@ -1906,7 +1909,7 @@ static int cx231xx_close(struct file *filp) } v4l2_fh_exit(&fh->fh); kfree(fh); - wake_up_interruptible_nr(&dev->open, 1); + wake_up_interruptible(&dev->open); return 0; } diff --git a/kernel/drivers/media/usb/cx231xx/cx231xx.h b/kernel/drivers/media/usb/cx231xx/cx231xx.h index 00d3bce9a..54790fbe8 100644 --- a/kernel/drivers/media/usb/cx231xx/cx231xx.h +++ b/kernel/drivers/media/usb/cx231xx/cx231xx.h @@ -77,6 +77,7 @@ #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19 #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20 #define CX231XX_BOARD_HAUPPAUGE_955Q 21 +#define CX231XX_BOARD_TERRATEC_GRABBY 22 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 diff --git a/kernel/drivers/media/usb/dvb-usb-v2/af9015.c b/kernel/drivers/media/usb/dvb-usb-v2/af9015.c index 16c0b7d4f..95a7388e8 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/af9015.c @@ -641,7 +641,7 @@ static int af9015_af9013_set_frontend(struct dvb_frontend *fe) /* override demod callbacks for resource locking */ static int af9015_af9013_read_status(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { int ret; struct af9015_state *state = fe_to_priv(fe); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/af9015.h b/kernel/drivers/media/usb/dvb-usb-v2/af9015.h index 3a6f3ad1e..1db1bb0d5 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/af9015.h +++ b/kernel/drivers/media/usb/dvb-usb-v2/af9015.h @@ -133,7 +133,7 @@ struct af9015_state { /* for demod callback override */ int (*set_frontend[2]) (struct dvb_frontend *fe); - int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status); + int (*read_status[2]) (struct dvb_frontend *fe, enum fe_status *status); int (*init[2]) (struct dvb_frontend *fe); int (*sleep[2]) (struct dvb_frontend *fe); int (*tuner_init[2]) (struct dvb_frontend *fe); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/af9035.c b/kernel/drivers/media/usb/dvb-usb-v2/af9035.c index 80a29f537..6e02a15d3 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1234,10 +1234,6 @@ static int af9035_frontend_detach(struct dvb_usb_adapter *adap) return 0; } -static struct tua9001_config af9035_tua9001_config = { - .i2c_addr = 0x60, -}; - static const struct fc0011_config af9035_fc0011_config = { .i2c_address = 0x60, }; @@ -1265,11 +1261,6 @@ static struct tda18218_config af9035_tda18218_config = { .i2c_wr_max = 21, }; -static const struct fc2580_config af9035_fc2580_config = { - .i2c_addr = 0x56, - .clock = 16384000, -}; - static const struct fc0012_config af9035_fc0012_config[] = { { .i2c_address = 0x63, @@ -1301,9 +1292,15 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) */ switch (state->af9033_config[adap->id].tuner) { - case AF9033_TUNER_TUA9001: - /* AF9035 gpiot3 = TUA9001 RESETN - AF9035 gpiot2 = TUA9001 RXEN */ + case AF9033_TUNER_TUA9001: { + struct tua9001_platform_data tua9001_pdata = { + .dvb_frontend = adap->fe[0], + }; + + /* + * AF9035 gpiot3 = TUA9001 RESETN + * AF9035 gpiot2 = TUA9001 RXEN + */ /* configure gpiot2 and gpiot2 as output */ ret = af9035_wr_reg_mask(d, 0x00d8ec, 0x01, 0x01); @@ -1323,9 +1320,14 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) goto err; /* attach tuner */ - fe = dvb_attach(tua9001_attach, adap->fe[0], - &d->i2c_adap, &af9035_tua9001_config); + ret = af9035_add_i2c_dev(d, "tua9001", 0x60, &tua9001_pdata, + &d->i2c_adap); + if (ret) + goto err; + + fe = adap->fe[0]; break; + } case AF9033_TUNER_FC0011: fe = dvb_attach(fc0011_attach, adap->fe[0], &d->i2c_adap, &af9035_fc0011_config); @@ -1390,7 +1392,11 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(tda18218_attach, adap->fe[0], &d->i2c_adap, &af9035_tda18218_config); break; - case AF9033_TUNER_FC2580: + case AF9033_TUNER_FC2580: { + struct fc2580_platform_data fc2580_pdata = { + .dvb_frontend = adap->fe[0], + }; + /* Tuner enable using gpiot2_o, gpiot2_en and gpiot2_on */ ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); if (ret < 0) @@ -1406,9 +1412,14 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) usleep_range(10000, 50000); /* attach tuner */ - fe = dvb_attach(fc2580_attach, adap->fe[0], - &d->i2c_adap, &af9035_fc2580_config); + ret = af9035_add_i2c_dev(d, "fc2580", 0x56, &fc2580_pdata, + &d->i2c_adap); + if (ret) + goto err; + + fe = adap->fe[0]; break; + } case AF9033_TUNER_FC0012: /* * AF9035 gpiot2 = FC0012 enable @@ -1569,6 +1580,7 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; + si2157_config.if_port = 1; ret = af9035_add_i2c_dev(d, "si2157", 0x63, &si2157_config, state->i2c_adapter_demod); @@ -1611,6 +1623,8 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap) dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id); switch (state->af9033_config[adap->id].tuner) { + case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC2580: case AF9033_TUNER_IT9135_38: case AF9033_TUNER_IT9135_51: case AF9033_TUNER_IT9135_52: @@ -2021,6 +2035,9 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "Asus U3100Mini Plus", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa, &af9035_props, "TerraTec Cinergy T Stick (rev. 2)", NULL) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, 0x0337, + &af9035_props, "AVerMedia HD Volar (A867)", NULL) }, + /* IT9135 devices */ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135, &af9035_props, "ITE 9135 Generic", RC_MAP_IT913X_V1) }, @@ -2046,9 +2063,6 @@ static const struct usb_device_id af9035_id_table[] = { { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2, &af9035_props, "Digital Dual TV Receiver CTVDIGDUAL_V2", RC_MAP_IT913X_V1) }, - /* IT930x devices */ - { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9303, - &it930x_props, "ITE 9303 Generic", NULL) }, /* XXX: that same ID [0ccd:0099] is used by af9015 driver too */ { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099, &af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", @@ -2061,6 +2075,10 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "PCTV AndroiDTV (78e)", RC_MAP_IT913X_V1) }, { DVB_USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_79E, &af9035_props, "PCTV microStick (79e)", RC_MAP_IT913X_V2) }, + + /* IT930x devices */ + { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9303, + &it930x_props, "ITE 9303 Generic", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/dvbsky.c b/kernel/drivers/media/usb/dvb-usb-v2/dvbsky.c index cdf59bcd7..1dd962535 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -45,9 +45,9 @@ struct dvbsky_state { /* fe hook functions*/ int (*fe_set_voltage)(struct dvb_frontend *fe, - fe_sec_voltage_t voltage); + enum fe_sec_voltage voltage); int (*fe_read_status)(struct dvb_frontend *fe, - fe_status_t *status); + enum fe_status *status); }; static int dvbsky_usb_generic_rw(struct dvb_usb_device *d, @@ -237,7 +237,7 @@ static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) #endif static int dvbsky_usb_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct dvb_usb_device *d = fe_to_d(fe); struct dvbsky_state *state = d_to_priv(d); @@ -277,7 +277,8 @@ static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6]) return 0; } -static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int dvbsky_usb_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct dvb_usb_device *d = fe_to_d(fe); struct dvbsky_state *state = d_to_priv(d); @@ -331,6 +332,7 @@ static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) /* attach tuner */ ts2020_config.fe = adap->fe[0]; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; strlcpy(info.type, "ts2020", I2C_NAME_SIZE); info.addr = 0x60; info.platform_data = &ts2020_config; @@ -368,7 +370,7 @@ fail_attach: } static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct dvb_usb_device *d = fe_to_d(fe); struct dvbsky_state *state = d_to_priv(d); @@ -453,6 +455,7 @@ static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap) /* attach tuner */ ts2020_config.fe = adap->fe[0]; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; strlcpy(info.type, "ts2020", I2C_NAME_SIZE); info.addr = 0x60; info.platform_data = &ts2020_config; @@ -549,6 +552,7 @@ static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -615,7 +619,8 @@ static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &i2c_adapter; si2168_config.fe = &adap->fe[0]; - si2168_config.ts_mode = SI2168_TS_PARALLEL | 0x40; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + si2168_config.ts_clock_gapped = true; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2168", I2C_NAME_SIZE); info.addr = 0x64; @@ -632,6 +637,7 @@ static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -841,6 +847,10 @@ static const struct usb_device_id dvbsky_id_table[] = { USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI, &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI", RC_MAP_TT_1500) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, + USB_PID_TERRATEC_H7_3, + &dvbsky_t680c_props, "Terratec H7 Rev.4", + RC_MAP_TT_1500) }, { } }; MODULE_DEVICE_TABLE(usb, dvbsky_id_table); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/lmedm04.c b/kernel/drivers/media/usb/dvb-usb-v2/lmedm04.c index 5de6f7c04..3721ee63b 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -126,7 +126,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct lme2510_state { unsigned long int_urb_due; - fe_status_t lock_status; + enum fe_status lock_status; u8 id; u8 tuner_config; u8 signal_level; @@ -144,12 +144,12 @@ struct lme2510_state { struct urb *lme_urb; void *usb_buffer; /* Frontend original calls */ - int (*fe_read_status)(struct dvb_frontend *, fe_status_t *); + int (*fe_read_status)(struct dvb_frontend *, enum fe_status *); int (*fe_read_signal_strength)(struct dvb_frontend *, u16 *); int (*fe_read_snr)(struct dvb_frontend *, u16 *); int (*fe_read_ber)(struct dvb_frontend *, u32 *); int (*fe_read_ucblocks)(struct dvb_frontend *, u32 *); - int (*fe_set_voltage)(struct dvb_frontend *, fe_sec_voltage_t); + int (*fe_set_voltage)(struct dvb_frontend *, enum fe_sec_voltage); u8 dvb_usb_lme2510_firmware; }; @@ -257,6 +257,62 @@ static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) return ret; } +/* Convert range from 0x00-0xff to 0x0000-0xffff */ +#define reg_to_16bits(x) ((x) | ((x) << 8)) + +static void lme2510_update_stats(struct dvb_usb_adapter *adap) +{ + struct lme2510_state *st = adap_to_priv(adap); + struct dvb_frontend *fe = adap->fe[0]; + struct dtv_frontend_properties *c; + u32 s_tmp = 0, c_tmp = 0; + + if (!fe) + return; + + c = &fe->dtv_property_cache; + + c->block_count.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (st->i2c_talk_onoff) { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + switch (st->tuner_config) { + case TUNER_LG: + s_tmp = reg_to_16bits(0xff - st->signal_level); + c_tmp = reg_to_16bits(0xff - st->signal_sn); + break; + case TUNER_S7395: + case TUNER_S0194: + s_tmp = 0xffff - (((st->signal_level * 2) << 8) * 5 / 4); + c_tmp = reg_to_16bits((0xff - st->signal_sn - 0xa1) * 3); + break; + case TUNER_RS2000: + s_tmp = reg_to_16bits(st->signal_level); + c_tmp = reg_to_16bits(st->signal_sn); + } + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = (u64)s_tmp; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_RELATIVE; + c->cnr.stat[0].uvalue = (u64)c_tmp; +} + static void lme2510_int_response(struct urb *lme_urb) { struct dvb_usb_adapter *adap = lme_urb->context; @@ -292,15 +348,16 @@ static void lme2510_int_response(struct urb *lme_urb) switch (ibuf[0]) { case 0xaa: debug_data_snipet(1, "INT Remote data snipet", ibuf); - if ((ibuf[4] + ibuf[5]) == 0xff) { - key = RC_SCANCODE_NECX((ibuf[2] ^ 0xff) << 8 | - (ibuf[3] > 0) ? (ibuf[3] ^ 0xff) : 0, - ibuf[5]); - deb_info(1, "INT Key =%08x", key); - if (adap_to_d(adap)->rc_dev != NULL) - rc_keydown(adap_to_d(adap)->rc_dev, - RC_TYPE_NEC, key, 0); - } + if (!adap_to_d(adap)->rc_dev) + break; + + key = RC_SCANCODE_NEC32(ibuf[2] << 24 | + ibuf[3] << 16 | + ibuf[4] << 8 | + ibuf[5]); + + deb_info(1, "INT Key = 0x%08x", key); + rc_keydown(adap_to_d(adap)->rc_dev, RC_TYPE_NEC, key, 0); break; case 0xbb: switch (st->tuner_config) { @@ -337,6 +394,8 @@ static void lme2510_int_response(struct urb *lme_urb) if (!signal_lock) st->lock_status &= ~FE_HAS_LOCK; + lme2510_update_stats(adap); + debug_data_snipet(5, "INT Remote data snipet in", ibuf); break; case 0xcc: @@ -799,10 +858,11 @@ static struct m88rs2000_config m88rs2000_config = { static struct ts2020_config ts2020_config = { .tuner_address = 0x60, .clk_out_div = 7, + .dont_poll = true }; static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { struct dvb_usb_device *d = fe_to_d(fe); struct lme2510_state *st = fe_to_priv(fe); @@ -837,7 +897,7 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, return (ret < 0) ? -ENODEV : 0; } -static int dm04_read_status(struct dvb_frontend *fe, fe_status_t *status) +static int dm04_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct dvb_usb_device *d = fe_to_d(fe); struct lme2510_state *st = d->priv; @@ -871,56 +931,45 @@ static int dm04_read_status(struct dvb_frontend *fe, fe_status_t *status) *status = st->lock_status; - if (!(*status & FE_HAS_LOCK)) + if (!(*status & FE_HAS_LOCK)) { + struct dvb_usb_adapter *adap = fe_to_adap(fe); + st->i2c_talk_onoff = 1; + lme2510_update_stats(adap); + } + return ret; } static int dm04_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct lme2510_state *st = fe_to_priv(fe); if (st->fe_read_signal_strength && !st->stream_on) return st->fe_read_signal_strength(fe, strength); - switch (st->tuner_config) { - case TUNER_LG: - *strength = 0xff - st->signal_level; - *strength |= *strength << 8; - break; - /* fall through */ - case TUNER_S7395: - case TUNER_S0194: - *strength = 0xffff - (((st->signal_level * 2) << 8) * 5 / 4); - break; - case TUNER_RS2000: - *strength = (u16)((u32)st->signal_level * 0xffff / 0xff); - } + if (c->strength.stat[0].scale == FE_SCALE_RELATIVE) + *strength = (u16)c->strength.stat[0].uvalue; + else + *strength = 0; return 0; } static int dm04_read_snr(struct dvb_frontend *fe, u16 *snr) { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct lme2510_state *st = fe_to_priv(fe); if (st->fe_read_snr && !st->stream_on) return st->fe_read_snr(fe, snr); - switch (st->tuner_config) { - case TUNER_LG: - *snr = 0xff - st->signal_sn; - *snr |= *snr << 8; - break; - /* fall through */ - case TUNER_S7395: - case TUNER_S0194: - *snr = (u16)((0xff - st->signal_sn - 0xa1) * 3) << 8; - break; - case TUNER_RS2000: - *snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f); - } + if (c->cnr.stat[0].scale == FE_SCALE_RELATIVE) + *snr = (u16)c->cnr.stat[0].uvalue; + else + *snr = 0; return 0; } @@ -1296,7 +1345,7 @@ module_usb_driver(lme2510_driver); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("2.06"); +MODULE_VERSION("2.07"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(LME2510_C_S7395); MODULE_FIRMWARE(LME2510_C_LG); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index ecefa5c47..ea3753653 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -72,7 +72,7 @@ int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, static int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, - fe_code_rate_t *code_rate) + enum fe_code_rate *code_rate) { u8 val; int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); @@ -103,7 +103,7 @@ fail: static int mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state, - fe_modulation_t *modulation) + enum fe_modulation *modulation) { u8 val; int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); @@ -128,7 +128,7 @@ fail: static int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, - fe_transmit_mode_t *fft_mode) + enum fe_transmit_mode *fft_mode) { u8 val; int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); @@ -153,7 +153,7 @@ fail: static int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, - fe_guard_interval_t *guard) + enum fe_guard_interval *guard) { u8 val; int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); @@ -181,7 +181,7 @@ fail: static int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, - fe_hierarchy_t *hierarchy) + enum fe_hierarchy *hierarchy) { u8 val; int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); @@ -441,7 +441,7 @@ fail: } static int mxl111sf_demod_read_status(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { struct mxl111sf_demod_state *state = fe->demodulator_priv; int ret, locked, cr_lock, sync_lock, fec_lock; @@ -480,7 +480,7 @@ static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { struct mxl111sf_demod_state *state = fe->demodulator_priv; - fe_modulation_t modulation; + enum fe_modulation modulation; u16 snr; mxl111sf_demod_calc_snr(state, &snr); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 895441fe9..5a503a6bb 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -34,6 +34,14 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) unsigned int pipe; u8 requesttype; + mutex_lock(&d->usb_mutex); + + if (req->size > sizeof(dev->buf)) { + dev_err(&d->intf->dev, "too large message %u\n", req->size); + ret = -EINVAL; + goto err_mutex_unlock; + } + if (req->index & CMD_WR_FLAG) { /* write */ memcpy(dev->buf, req->data, req->size); @@ -50,14 +58,17 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, req->index, dev->buf, req->size); if (ret < 0) - goto err; + goto err_mutex_unlock; /* read request, copy returned data to return buf */ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) memcpy(req->data, dev->buf, req->size); + mutex_unlock(&d->usb_mutex); + return 0; -err: +err_mutex_unlock: + mutex_unlock(&d->usb_mutex); dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -217,7 +228,7 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], req.data = &msg[0].buf[1]; ret = rtl28xxu_ctrl_msg(d, &req); } - } else if (msg[0].len < 23) { + } else if ((msg[0].len < 23) && (!dev->new_i2c_write)) { /* method 2 - old I2C */ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); req.index = CMD_I2C_WR; @@ -232,8 +243,14 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], req.data = msg[0].buf; ret = rtl28xxu_ctrl_msg(d, &req); } + } else if (num == 1 && (msg[0].flags & I2C_M_RD)) { + req.value = (msg[0].addr << 1); + req.index = CMD_I2C_DA_RD; + req.size = msg[0].len; + req.data = msg[0].buf; + ret = rtl28xxu_ctrl_msg(d, &req); } else { - ret = -EINVAL; + ret = -EOPNOTSUPP; } err_mutex_unlock: @@ -357,6 +374,8 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) struct rtl28xxu_req req_r828d = {0x0074, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_mn88472 = {0xff38, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_si2157 = {0x00c0, CMD_I2C_RD, 1, buf}; + struct rtl28xxu_req req_si2168 = {0x00c8, CMD_I2C_RD, 1, buf}; dev_dbg(&d->intf->dev, "\n"); @@ -477,6 +496,35 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) goto tuner_found; } + /* GPIO0 and GPIO5 to reset Si2157/Si2168 tuner and demod */ + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x00, 0x21); + if (ret) + goto err; + + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x00, 0x21); + if (ret) + goto err; + + msleep(50); + + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x21, 0x21); + if (ret) + goto err; + + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x21, 0x21); + if (ret) + goto err; + + msleep(50); + + /* check Si2157 ID register; reg=c0 val=80 */ + ret = rtl28xxu_ctrl_msg(d, &req_si2157); + if (ret == 0 && ((buf[0] & 0x80) == 0x80)) { + dev->tuner = TUNER_RTL2832_SI2157; + dev->tuner_name = "SI2157"; + goto tuner_found; + } + tuner_found: dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name); @@ -510,6 +558,15 @@ tuner_found: goto demod_found; } } + if (dev->tuner == TUNER_RTL2832_SI2157) { + /* check Si2168 ID register; reg=c8 val=80 */ + ret = rtl28xxu_ctrl_msg(d, &req_si2168); + if (ret == 0 && ((buf[0] & 0x80) == 0x80)) { + dev_dbg(&d->intf->dev, "Si2168 found\n"); + dev->slave_demod = SLAVE_DEMOD_SI2168; + goto demod_found; + } + } demod_found: /* close demod I2C gate */ @@ -643,6 +700,11 @@ err: return ret; } +static const struct rtl2832_platform_data rtl2832_fc2580_platform_data = { + .clk = 28800000, + .tuner = TUNER_RTL2832_FC2580, +}; + static const struct rtl2832_platform_data rtl2832_fc0012_platform_data = { .clk = 28800000, .tuner = TUNER_RTL2832_FC0012 @@ -668,6 +730,11 @@ static const struct rtl2832_platform_data rtl2832_r820t_platform_data = { .tuner = TUNER_RTL2832_R820T, }; +static const struct rtl2832_platform_data rtl2832_si2157_platform_data = { + .clk = 28800000, + .tuner = TUNER_RTL2832_SI2157, +}; + static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -804,8 +871,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) *pdata = rtl2832_fc0013_platform_data; break; case TUNER_RTL2832_FC2580: - /* FIXME: do not abuse fc0012 settings */ - *pdata = rtl2832_fc0012_platform_data; + *pdata = rtl2832_fc2580_platform_data; break; case TUNER_RTL2832_TUA9001: *pdata = rtl2832_tua9001_platform_data; @@ -817,6 +883,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) case TUNER_RTL2832_R828D: *pdata = rtl2832_r820t_platform_data; break; + case TUNER_RTL2832_SI2157: + *pdata = rtl2832_si2157_platform_data; + break; default: dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name); ret = -ENODEV; @@ -884,7 +953,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) } dev->i2c_client_slave_demod = client; - } else { + } else if (dev->slave_demod == SLAVE_DEMOD_MN88473) { struct mn88473_config mn88473_config = {}; mn88473_config.fe = &adap->fe[1]; @@ -906,9 +975,37 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) } dev->i2c_client_slave_demod = client; + } else { + struct si2168_config si2168_config = {}; + struct i2c_adapter *adapter; + + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &adap->fe[1]; + si2168_config.ts_mode = SI2168_TS_SERIAL; + si2168_config.ts_clock_inv = false; + si2168_config.ts_clock_gapped = true; + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client = i2c_new_device(&d->i2c_adap, &info); + if (client == NULL || client->dev.driver == NULL) { + dev->slave_demod = SLAVE_DEMOD_NONE; + goto err_slave_demod_failed; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dev->slave_demod = SLAVE_DEMOD_NONE; + goto err_slave_demod_failed; + } + + dev->i2c_client_slave_demod = client; + + /* for Si2168 devices use only new I2C write method */ + dev->new_i2c_write = true; } } - return 0; err_slave_demod_failed: err: @@ -1018,15 +1115,6 @@ err: return ret; } -static const struct fc2580_config rtl2832u_fc2580_config = { - .i2c_addr = 0x56, - .clock = 16384000, -}; - -static struct tua9001_config rtl2832u_tua9001_config = { - .i2c_addr = 0x60, -}; - static const struct fc0012_config rtl2832u_fc0012_config = { .i2c_address = 0x63, /* 0xc6 >> 1 */ .xtal_freq = FC_XTAL_28_8_MHZ, @@ -1105,12 +1193,34 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) subdev = i2c_get_clientdata(client); } break; - case TUNER_RTL2832_FC2580: - fe = dvb_attach(fc2580_attach, adap->fe[0], - dev->demod_i2c_adapter, - &rtl2832u_fc2580_config); + case TUNER_RTL2832_FC2580: { + struct fc2580_platform_data fc2580_pdata = { + .dvb_frontend = adap->fe[0], + }; + struct i2c_board_info board_info = {}; + + strlcpy(board_info.type, "fc2580", I2C_NAME_SIZE); + board_info.addr = 0x56; + board_info.platform_data = &fc2580_pdata; + request_module("fc2580"); + client = i2c_new_device(dev->demod_i2c_adapter, + &board_info); + if (client == NULL || client->dev.driver == NULL) + break; + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + break; + } + dev->i2c_client_tuner = client; + subdev = fc2580_pdata.get_v4l2_subdev(client); + } break; - case TUNER_RTL2832_TUA9001: + case TUNER_RTL2832_TUA9001: { + struct tua9001_platform_data tua9001_pdata = { + .dvb_frontend = adap->fe[0], + }; + struct i2c_board_info board_info = {}; + /* enable GPIO1 and GPIO4 as output */ ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12); if (ret) @@ -1120,10 +1230,20 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) if (ret) goto err; - fe = dvb_attach(tua9001_attach, adap->fe[0], - dev->demod_i2c_adapter, - &rtl2832u_tua9001_config); + strlcpy(board_info.type, "tua9001", I2C_NAME_SIZE); + board_info.addr = 0x60; + board_info.platform_data = &tua9001_pdata; + request_module("tua9001"); + client = i2c_new_device(dev->demod_i2c_adapter, &board_info); + if (client == NULL || client->dev.driver == NULL) + break; + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + break; + } + dev->i2c_client_tuner = client; break; + } case TUNER_RTL2832_R820T: fe = dvb_attach(r820t_attach, adap->fe[0], dev->demod_i2c_adapter, @@ -1148,6 +1268,39 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) adap->fe[1]->ops.tuner_ops.get_rf_strength; } break; + case TUNER_RTL2832_SI2157: { + struct si2157_config si2157_config = { + .fe = adap->fe[0], + .if_port = 0, + .inversion = false, + }; + + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client = i2c_new_device(&d->i2c_adap, &info); + if (client == NULL || client->dev.driver == NULL) + break; + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + break; + } + + dev->i2c_client_tuner = client; + subdev = i2c_get_clientdata(client); + + /* copy tuner ops for 2nd FE as tuner is shared */ + if (adap->fe[1]) { + adap->fe[1]->tuner_priv = + adap->fe[0]->tuner_priv; + memcpy(&adap->fe[1]->ops.tuner_ops, + &adap->fe[0]->ops.tuner_ops, + sizeof(struct dvb_tuner_ops)); + } + } + break; default: dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner); } @@ -1158,6 +1311,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) /* register SDR */ switch (dev->tuner) { + case TUNER_RTL2832_FC2580: case TUNER_RTL2832_FC0012: case TUNER_RTL2832_FC0013: case TUNER_RTL2832_E4000: @@ -1178,7 +1332,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) "rtl2832_sdr", PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); - if (pdev == NULL || pdev->dev.driver == NULL) + if (IS_ERR(pdev) || pdev->dev.driver == NULL) break; dev->platform_device_sdr = pdev; break; @@ -1742,6 +1896,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl28xxu_props, "MSI DIGIVOX Micro HD", NULL) }, { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620, &rtl28xxu_props, "Compro VideoMate U620F", NULL) }, + { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0650, + &rtl28xxu_props, "Compro VideoMate U650F", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394, &rtl28xxu_props, "MaxMedia HU394-T", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03, @@ -1764,6 +1920,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { /* RTL2832P devices: */ { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, &rtl28xxu_props, "Astrometa DVB-T2", NULL) }, + { DVB_USB_DEVICE(0x5654, 0xca42, + &rtl28xxu_props, "GoTView MasterHD 3", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 1b5d7ffb6..138062960 100644 --- a/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/kernel/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -41,6 +41,8 @@ #include "fc2580.h" #include "tua9001.h" #include "r820t.h" +#include "si2168.h" +#include "si2157.h" /* * USB commands @@ -69,13 +71,14 @@ struct rtl28xxu_dev { - u8 buf[28]; + u8 buf[128]; u8 chip_id; u8 tuner; char *tuner_name; u8 page; /* integrated demod active register page */ struct i2c_adapter *demod_i2c_adapter; bool rc_active; + bool new_i2c_write; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; struct i2c_client *i2c_client_slave_demod; @@ -83,6 +86,7 @@ struct rtl28xxu_dev { #define SLAVE_DEMOD_NONE 0 #define SLAVE_DEMOD_MN88472 1 #define SLAVE_DEMOD_MN88473 2 + #define SLAVE_DEMOD_SI2168 3 unsigned int slave_demod:2; union { struct rtl2830_platform_data rtl2830_platform_data; @@ -116,6 +120,7 @@ enum rtl28xxu_tuner { TUNER_RTL2832_FC0013, TUNER_RTL2832_R820T, TUNER_RTL2832_R828D, + TUNER_RTL2832_SI2157, }; struct rtl28xxu_req { diff --git a/kernel/drivers/media/usb/dvb-usb/af9005-fe.c b/kernel/drivers/media/usb/dvb-usb/af9005-fe.c index 740f3f496..ac97075d7 100644 --- a/kernel/drivers/media/usb/dvb-usb/af9005-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/af9005-fe.c @@ -29,7 +29,7 @@ struct af9005_fe_state { struct dvb_usb_device *d; - fe_status_t stat; + enum fe_status stat; /* retraining parameters */ u32 original_fcw; @@ -437,7 +437,8 @@ static int af9005_fe_refresh_state(struct dvb_frontend *fe) return 0; } -static int af9005_fe_read_status(struct dvb_frontend *fe, fe_status_t * stat) +static int af9005_fe_read_status(struct dvb_frontend *fe, + enum fe_status *stat) { struct af9005_fe_state *state = fe->demodulator_priv; u8 temp; @@ -481,7 +482,7 @@ static int af9005_fe_read_status(struct dvb_frontend *fe, fe_status_t * stat) return ret; if (temp != state->strong) { deb_info("adjust for strong signal %d\n", temp); - state->strong = temp; + state->strong = temp; } return 0; } diff --git a/kernel/drivers/media/usb/dvb-usb/az6027.c b/kernel/drivers/media/usb/dvb-usb/az6027.c index 0df52ab32..92e47d6c3 100644 --- a/kernel/drivers/media/usb/dvb-usb/az6027.c +++ b/kernel/drivers/media/usb/dvb-usb/az6027.c @@ -778,7 +778,8 @@ static int az6027_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) } */ -static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int az6027_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { u8 buf; diff --git a/kernel/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/kernel/drivers/media/usb/dvb-usb/cinergyT2-fe.c index c890fe46a..b3ec743a7 100644 --- a/kernel/drivers/media/usb/dvb-usb/cinergyT2-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/cinergyT2-fe.c @@ -142,7 +142,7 @@ struct cinergyt2_fe_state { }; static int cinergyt2_fe_read_status(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { struct cinergyt2_fe_state *state = fe->demodulator_priv; struct dvbt_get_status_msg result; diff --git a/kernel/drivers/media/usb/dvb-usb/cxusb.c b/kernel/drivers/media/usb/dvb-usb/cxusb.c index ffc3704ab..ab7151181 100644 --- a/kernel/drivers/media/usb/dvb-usb/cxusb.c +++ b/kernel/drivers/media/usb/dvb-usb/cxusb.c @@ -1350,6 +1350,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe_adap[0].fe; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; diff --git a/kernel/drivers/media/usb/dvb-usb/dib0700.h b/kernel/drivers/media/usb/dvb-usb/dib0700.h index 927617d95..8fd8f5b48 100644 --- a/kernel/drivers/media/usb/dvb-usb/dib0700.h +++ b/kernel/drivers/media/usb/dvb-usb/dib0700.h @@ -48,7 +48,7 @@ struct dib0700_state { u8 disable_streaming_master_mode; u32 fw_version; u32 nb_packet_buffer_size; - int (*read_status)(struct dvb_frontend *, fe_status_t *); + int (*read_status)(struct dvb_frontend *, enum fe_status *); int (*sleep)(struct dvb_frontend* fe); u8 buf[255]; }; diff --git a/kernel/drivers/media/usb/dvb-usb/dib0700_devices.c b/kernel/drivers/media/usb/dvb-usb/dib0700_devices.c index c17052322..7ed49646a 100644 --- a/kernel/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/kernel/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -861,22 +861,22 @@ static int dib7770_set_param_override(struct dvb_frontend *fe) struct dvb_usb_adapter *adap = fe->dvb->priv; struct dib0700_adapter_state *state = adap->priv; - u16 offset; - u8 band = BAND_OF_FREQUENCY(p->frequency/1000); - switch (band) { - case BAND_VHF: - state->dib7000p_ops.set_gpio(fe, 0, 0, 1); - offset = 850; - break; - case BAND_UHF: - default: - state->dib7000p_ops.set_gpio(fe, 0, 0, 0); - offset = 250; - break; - } - deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); - state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); - return state->set_param_save(fe); + u16 offset; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + switch (band) { + case BAND_VHF: + state->dib7000p_ops.set_gpio(fe, 0, 0, 1); + offset = 850; + break; + case BAND_UHF: + default: + state->dib7000p_ops.set_gpio(fe, 0, 0, 0); + offset = 250; + break; + } + deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); + state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); + return state->set_param_save(fe); } static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) @@ -3309,7 +3309,7 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) } static int novatd_read_status_override(struct dvb_frontend *fe, - fe_status_t *stat) + enum fe_status *stat) { struct dvb_usb_adapter *adap = fe->dvb->priv; struct dvb_usb_device *dev = adap->dev; @@ -3821,6 +3821,10 @@ MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); } \ } +#define DIB0700_NUM_FRONTENDS(n) \ + .num_frontends = n, \ + .size_of_priv = sizeof(struct dib0700_adapter_state) + struct dvb_usb_device_properties dib0700_devices[] = { { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -3828,7 +3832,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3839,7 +3843,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, @@ -3893,7 +3896,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .frontend_attach = bristol_frontend_attach, .tuner_attach = bristol_tuner_attach, @@ -3901,7 +3904,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .frontend_attach = bristol_frontend_attach, .tuner_attach = bristol_tuner_attach, @@ -3933,7 +3936,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3944,10 +3947,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -3958,8 +3959,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), } }, @@ -4002,7 +4001,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4013,8 +4012,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, }, @@ -4049,7 +4046,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4060,7 +4057,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, @@ -4131,7 +4127,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4142,7 +4138,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, }, @@ -4177,7 +4172,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4188,9 +4183,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4201,7 +4195,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), } }, @@ -4236,7 +4229,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4247,9 +4240,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4260,7 +4252,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), } }, @@ -4304,7 +4295,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4315,9 +4306,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4328,7 +4318,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = sizeof(struct dib0700_adapter_state), } }, @@ -4355,7 +4344,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4366,8 +4355,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, }, @@ -4425,15 +4412,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .frontend_attach = s5h1411_frontend_attach, .tuner_attach = xc5000_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, }, @@ -4463,15 +4448,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .frontend_attach = lgdt3305_frontend_attach, .tuner_attach = mxl5007t_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, }, @@ -4491,7 +4474,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4502,8 +4485,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4543,7 +4524,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4554,8 +4535,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4589,7 +4568,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4600,11 +4579,9 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, @@ -4615,8 +4592,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4642,7 +4617,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4654,8 +4629,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4681,7 +4654,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4693,8 +4666,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4720,7 +4691,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4732,8 +4703,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4759,7 +4728,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4771,8 +4740,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4798,7 +4765,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4810,8 +4777,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4837,7 +4802,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4849,11 +4814,9 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4865,8 +4828,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4892,15 +4853,13 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .frontend_attach = pctv340e_frontend_attach, .tuner_attach = xc4000_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }}, - .size_of_priv = sizeof(struct - dib0700_adapter_state), }, }, @@ -4929,7 +4888,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4941,9 +4900,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x03), } }, - - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, @@ -4969,7 +4925,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { - .num_frontends = 1, + DIB0700_NUM_FRONTENDS(1), .fe = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, @@ -4982,9 +4938,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { DIB0700_DEFAULT_STREAMING_CONFIG(0x02), } }, - - .size_of_priv = - sizeof(struct dib0700_adapter_state), }, }, diff --git a/kernel/drivers/media/usb/dvb-usb/dtt200u-fe.c b/kernel/drivers/media/usb/dvb-usb/dtt200u-fe.c index 3d81daa49..8637ad1be 100644 --- a/kernel/drivers/media/usb/dvb-usb/dtt200u-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/dtt200u-fe.c @@ -14,13 +14,14 @@ struct dtt200u_fe_state { struct dvb_usb_device *d; - fe_status_t stat; + enum fe_status stat; struct dtv_frontend_properties fep; struct dvb_frontend frontend; }; -static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) +static int dtt200u_fe_read_status(struct dvb_frontend *fe, + enum fe_status *stat) { struct dtt200u_fe_state *state = fe->demodulator_priv; u8 st = GET_TUNE_STATUS, b[3]; @@ -105,7 +106,7 @@ static int dtt200u_fe_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *fep = &fe->dtv_property_cache; struct dtt200u_fe_state *state = fe->demodulator_priv; int i; - fe_status_t st; + enum fe_status st; u16 freq = fep->frequency / 250000; u8 bwbuf[2] = { SET_BANDWIDTH, 0 },freqbuf[3] = { SET_RF_FREQ, 0, 0 }; diff --git a/kernel/drivers/media/usb/dvb-usb/dw2102.c b/kernel/drivers/media/usb/dvb-usb/dw2102.c index f1f357f43..14ef25dc6 100644 --- a/kernel/drivers/media/usb/dvb-usb/dw2102.c +++ b/kernel/drivers/media/usb/dvb-usb/dw2102.c @@ -117,8 +117,13 @@ struct dw2102_state { u8 initialized; + u8 last_lock; struct i2c_client *i2c_client_tuner; - int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v); + + /* fe hook functions*/ + int (*old_set_voltage)(struct dvb_frontend *f, enum fe_sec_voltage v); + int (*fe_read_status)(struct dvb_frontend *fe, + enum fe_status *status); }; /* debug */ @@ -437,7 +442,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i ibuf, msg[j].len + 2, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf + 2, msg[j].len); - mdelay(10); + mdelay(10); } else if (((msg[j].buf[0] == 0xb0) && (msg[j].addr == 0x68)) || ((msg[j].buf[0] == 0xf7) && @@ -928,8 +933,6 @@ static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) break; else mac[i] = ibuf[0]; - - debug_dump(mac, 6, printk); } return 0; @@ -946,7 +949,8 @@ static int su3000_identify_state(struct usb_device *udev, return 0; } -static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int dw210x_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { static u8 command_13v[] = {0x00, 0x01}; static u8 command_18v[] = {0x01, 0x01}; @@ -970,7 +974,8 @@ static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } -static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int s660_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct dvb_usb_adapter *d = (struct dvb_usb_adapter *)(fe->dvb->priv); @@ -1001,6 +1006,24 @@ static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon) i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); } +static int tt_s2_4600_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct dvb_usb_adapter *d = + (struct dvb_usb_adapter *)(fe->dvb->priv); + struct dw2102_state *st = (struct dw2102_state *)d->dev->priv; + int ret; + + ret = st->fe_read_status(fe, status); + + /* resync slave fifo when signal change from unlock to lock */ + if ((*status & FE_HAS_LOCK) && (!st->last_lock)) + su3000_streaming_ctrl(d, 1); + + st->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0; + return ret; +} + static struct stv0299_config sharp_z0194a_config = { .demod_address = 0x68, .inittab = sharp_z0194a_inittab, @@ -1553,6 +1576,12 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) state->i2c_client_tuner = client; + /* hook fe: need to resync the slave fifo when signal locks */ + state->fe_read_status = adap->fe_adap[0].fe->ops.read_status; + adap->fe_adap[0].fe->ops.read_status = tt_s2_4600_read_status; + + state->last_lock = 0; + return 0; } @@ -1657,6 +1686,8 @@ enum dw2102_table_entry { GOTVIEW_SAT_HD, GENIATECH_T220, TECHNOTREND_S2_4600, + TEVII_S482_1, + TEVII_S482_2, }; static struct usb_device_id dw2102_table[] = { @@ -1682,6 +1713,8 @@ static struct usb_device_id dw2102_table[] = { [GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)}, [TECHNOTREND_S2_4600] = {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_4600)}, + [TEVII_S482_1] = {USB_DEVICE(0x9022, 0xd483)}, + [TEVII_S482_2] = {USB_DEVICE(0x9022, 0xd484)}, { } }; @@ -2199,12 +2232,20 @@ static struct dvb_usb_device_properties tt_s2_4600_properties = { } }, } }, - .num_device_descs = 1, + .num_device_descs = 3, .devices = { { "TechnoTrend TT-connect S2-4600", { &dw2102_table[TECHNOTREND_S2_4600], NULL }, { NULL }, }, + { "TeVii S482 (tuner 1)", + { &dw2102_table[TEVII_S482_1], NULL }, + { NULL }, + }, + { "TeVii S482 (tuner 2)", + { &dw2102_table[TEVII_S482_2], NULL }, + { NULL }, + }, } }; diff --git a/kernel/drivers/media/usb/dvb-usb/friio-fe.c b/kernel/drivers/media/usb/dvb-usb/friio-fe.c index d56f927fc..8ec92fbea 100644 --- a/kernel/drivers/media/usb/dvb-usb/friio-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/friio-fe.c @@ -210,7 +210,8 @@ error: return -EREMOTEIO; } -static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) +static int jdvbt90502_read_status(struct dvb_frontend *fe, + enum fe_status *state) { u8 result; int ret; diff --git a/kernel/drivers/media/usb/dvb-usb/gp8psk-fe.c b/kernel/drivers/media/usb/dvb-usb/gp8psk-fe.c index 67957dd99..db6eb79cd 100644 --- a/kernel/drivers/media/usb/dvb-usb/gp8psk-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/gp8psk-fe.c @@ -51,7 +51,8 @@ static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) return 0; } -static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +static int gp8psk_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct gp8psk_fe_state *st = fe->demodulator_priv; gp8psk_fe_update_status(st); @@ -236,8 +237,8 @@ static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, return 0; } -static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe, - fe_sec_mini_cmd_t burst) +static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { struct gp8psk_fe_state *st = fe->demodulator_priv; u8 cmd; @@ -254,7 +255,8 @@ static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe, return 0; } -static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int gp8psk_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct gp8psk_fe_state* state = fe->demodulator_priv; @@ -265,7 +267,8 @@ static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return 0; } -static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct gp8psk_fe_state* state = fe->demodulator_priv; diff --git a/kernel/drivers/media/usb/dvb-usb/opera1.c b/kernel/drivers/media/usb/dvb-usb/opera1.c index 14a211991..2566d2f1c 100644 --- a/kernel/drivers/media/usb/dvb-usb/opera1.c +++ b/kernel/drivers/media/usb/dvb-usb/opera1.c @@ -167,7 +167,8 @@ static struct i2c_algorithm opera1_i2c_algo = { .functionality = opera1_i2c_func, }; -static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int opera1_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { static u8 command_13v[1]={0x00}; static u8 command_18v[1]={0x01}; diff --git a/kernel/drivers/media/usb/dvb-usb/pctv452e.c b/kernel/drivers/media/usb/dvb-usb/pctv452e.c index d17618fe8..ec397c4b7 100644 --- a/kernel/drivers/media/usb/dvb-usb/pctv452e.c +++ b/kernel/drivers/media/usb/dvb-usb/pctv452e.c @@ -611,7 +611,7 @@ static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) return 0; failed: - memset(mac, 0, 6); + eth_zero_addr(mac); return ret; } diff --git a/kernel/drivers/media/usb/dvb-usb/technisat-usb2.c b/kernel/drivers/media/usb/dvb-usb/technisat-usb2.c index 5801ae7f6..6c3c47722 100644 --- a/kernel/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/kernel/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -453,7 +453,7 @@ static struct stv090x_config technisat_usb2_stv090x_config; /* frontend attach */ static int technisat_usb2_set_voltage(struct dvb_frontend *fe, - fe_sec_voltage_t voltage) + enum fe_sec_voltage voltage) { int i; u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */ @@ -707,7 +707,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = { .stream = { .type = USB_ISOC, - .count = 8, + .count = 4, .endpoint = 0x2, .u = { .isoc = { diff --git a/kernel/drivers/media/usb/dvb-usb/vp702x-fe.c b/kernel/drivers/media/usb/dvb-usb/vp702x-fe.c index 5eab468dd..d361a72ca 100644 --- a/kernel/drivers/media/usb/dvb-usb/vp702x-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/vp702x-fe.c @@ -26,8 +26,8 @@ struct vp702x_fe_state { struct dvb_frontend_ops ops; - fe_sec_voltage_t voltage; - fe_sec_tone_mode_t tone_mode; + enum fe_sec_voltage voltage; + enum fe_sec_tone_mode tone_mode; u8 lnb_buf[8]; @@ -72,7 +72,8 @@ static u8 vp702x_chksum(u8 *buf,int f, int count) return ~s+1; } -static int vp702x_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +static int vp702x_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct vp702x_fe_state *st = fe->demodulator_priv; vp702x_fe_refresh_state(st); @@ -243,13 +244,15 @@ static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe, return 0; } -static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +static int vp702x_fe_send_diseqc_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) { deb_fe("%s\n",__func__); return 0; } -static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int vp702x_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct vp702x_fe_state *st = fe->demodulator_priv; struct vp702x_device_state *dst = st->d->priv; @@ -282,8 +285,8 @@ static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) return 0; } -static int vp702x_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t - voltage) +static int vp702x_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct vp702x_fe_state *st = fe->demodulator_priv; struct vp702x_device_state *dst = st->d->priv; diff --git a/kernel/drivers/media/usb/dvb-usb/vp702x.c b/kernel/drivers/media/usb/dvb-usb/vp702x.c index 22cf9f96c..ee1e19e36 100644 --- a/kernel/drivers/media/usb/dvb-usb/vp702x.c +++ b/kernel/drivers/media/usb/dvb-usb/vp702x.c @@ -259,12 +259,11 @@ static struct rc_map_table rc_map_vp702x_table[] = { /* remote control stuff (does not work with my box) */ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { +/* remove the following return to enabled remote querying */ +#if 0 u8 *key; int i; -/* remove the following return to enabled remote querying */ - return 0; - key = kmalloc(10, GFP_KERNEL); if (!key) return -ENOMEM; @@ -286,6 +285,8 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) break; } kfree(key); +#endif + return 0; } diff --git a/kernel/drivers/media/usb/dvb-usb/vp7045-fe.c b/kernel/drivers/media/usb/dvb-usb/vp7045-fe.c index b8825b18c..e708afc6a 100644 --- a/kernel/drivers/media/usb/dvb-usb/vp7045-fe.c +++ b/kernel/drivers/media/usb/dvb-usb/vp7045-fe.c @@ -26,7 +26,8 @@ struct vp7045_fe_state { struct dvb_usb_device *d; }; -static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +static int vp7045_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) { struct vp7045_fe_state *state = fe->demodulator_priv; u8 s0 = vp7045_read_reg(state->d,0x00), diff --git a/kernel/drivers/media/usb/em28xx/em28xx-camera.c b/kernel/drivers/media/usb/em28xx/em28xx-camera.c index a4b22c2c3..ed0b3a879 100644 --- a/kernel/drivers/media/usb/em28xx/em28xx-camera.c +++ b/kernel/drivers/media/usb/em28xx/em28xx-camera.c @@ -404,7 +404,9 @@ int em28xx_init_camera(struct em28xx *dev) .addr = client->addr, .platform_data = &camlink, }; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; /* * FIXME: sensor supports resolutions up to 1600x1200, but @@ -425,10 +427,10 @@ int em28xx_init_camera(struct em28xx *dev) break; } - fmt.code = MEDIA_BUS_FMT_YUYV8_2X8; - fmt.width = 640; - fmt.height = 480; - v4l2_subdev_call(subdev, video, s_mbus_fmt, &fmt); + format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; + format.format.width = 640; + format.format.height = 480; + v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format); /* NOTE: for UXGA=1600x1200 switch to 12MHz */ dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ; diff --git a/kernel/drivers/media/usb/em28xx/em28xx-dvb.c b/kernel/drivers/media/usb/em28xx/em28xx-dvb.c index a5b22c5a2..357be76c7 100644 --- a/kernel/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/kernel/drivers/media/usb/em28xx/em28xx-dvb.c @@ -96,6 +96,7 @@ struct em28xx_dvb { int lna_gpio; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; + struct i2c_client *i2c_client_sec; }; static inline void print_err_status(struct em28xx *dev, @@ -807,20 +808,6 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { .gate = TDA18271_GATE_DIGITAL, }; -static const struct tda10071_config em28xx_tda10071_config = { - .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ - .tuner_i2c_addr = 0x14, - .i2c_wr_max = 64, - .ts_mode = TDA10071_TS_SERIAL, - .spec_inv = 0, - .xtal = 40444000, /* 40.444 MHz */ - .pll_multiplier = 20, -}; - -static const struct a8293_config em28xx_a8293_config = { - .i2c_addr = 0x08, /* (0x10 >> 1) */ -}; - static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { .demod_address = (0x1e >> 1), .disable_i2c_gate_ctrl = 1, @@ -1331,16 +1318,60 @@ static int em28xx_dvb_init(struct em28xx *dev) &dev->i2c_adap[dev->def_i2c_bus], &c3tech_duo_tda18271_config); break; - case EM28174_BOARD_PCTV_460E: - /* attach demod */ - dvb->fe[0] = dvb_attach(tda10071_attach, - &em28xx_tda10071_config, &dev->i2c_adap[dev->def_i2c_bus]); + case EM28174_BOARD_PCTV_460E: { + struct i2c_client *client; + struct i2c_board_info board_info; + struct tda10071_platform_data tda10071_pdata = {}; + struct a8293_platform_data a8293_pdata = {}; + + /* attach demod + tuner combo */ + tda10071_pdata.clk = 40444000, /* 40.444 MHz */ + tda10071_pdata.i2c_wr_max = 64, + tda10071_pdata.ts_mode = TDA10071_TS_SERIAL, + tda10071_pdata.pll_multiplier = 20, + tda10071_pdata.tuner_i2c_addr = 0x14, + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "tda10071_cx24118", I2C_NAME_SIZE); + board_info.addr = 0x55; + board_info.platform_data = &tda10071_pdata; + request_module("tda10071"); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_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->fe[0] = tda10071_pdata.get_dvb_frontend(client); + dvb->i2c_client_demod = client; /* attach SEC */ - if (dvb->fe[0]) - dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], - &em28xx_a8293_config); + a8293_pdata.dvb_frontend = dvb->fe[0]; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "a8293", I2C_NAME_SIZE); + board_info.addr = 0x08; + board_info.platform_data = &a8293_pdata; + request_module("a8293"); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + dvb->i2c_client_sec = client; break; + } case EM2874_BOARD_DELOCK_61959: case EM2874_BOARD_MAXMEDIA_UB425_TC: /* attach demodulator */ @@ -1486,64 +1517,94 @@ static int em28xx_dvb_init(struct em28xx *dev) } } break; - case EM28178_BOARD_PCTV_461E: - { - /* demod I2C adapter */ - struct i2c_adapter *i2c_adapter; - struct i2c_client *client; - struct i2c_board_info info; - struct ts2020_config ts2020_config = { - }; - memset(&info, 0, sizeof(struct i2c_board_info)); - - /* attach demod */ - dvb->fe[0] = dvb_attach(m88ds3103_attach, - &pctv_461e_m88ds3103_config, - &dev->i2c_adap[dev->def_i2c_bus], - &i2c_adapter); - if (dvb->fe[0] == NULL) { - result = -ENODEV; - goto out_free; - } - - /* attach tuner */ - ts2020_config.fe = dvb->fe[0]; - strlcpy(info.type, "ts2022", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &ts2020_config; - request_module("ts2020"); - client = i2c_new_device(i2c_adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dvb->fe[0]); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(dvb->fe[0]); - result = -ENODEV; - goto out_free; - } + case EM28178_BOARD_PCTV_461E: { + struct i2c_client *client; + struct i2c_adapter *i2c_adapter; + struct i2c_board_info board_info; + struct m88ds3103_platform_data m88ds3103_pdata = {}; + struct ts2020_config ts2020_config = {}; + struct a8293_platform_data a8293_pdata = {}; - /* delegate signal strength measurement to tuner */ - dvb->fe[0]->ops.read_signal_strength = - dvb->fe[0]->ops.tuner_ops.get_rf_strength; + /* attach demod */ + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL; + m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk_pol = 1; + m88ds3103_pdata.agc = 0x99; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); + board_info.addr = 0x68; + board_info.platform_data = &m88ds3103_pdata; + request_module("m88ds3103"); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_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->fe[0] = m88ds3103_pdata.get_dvb_frontend(client); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(client); + dvb->i2c_client_demod = client; - /* attach SEC */ - if (!dvb_attach(a8293_attach, dvb->fe[0], - &dev->i2c_adap[dev->def_i2c_bus], - &em28xx_a8293_config)) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - dvb_frontend_detach(dvb->fe[0]); - result = -ENODEV; - goto out_free; - } + /* attach tuner */ + ts2020_config.fe = dvb->fe[0]; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "ts2022", I2C_NAME_SIZE); + board_info.addr = 0x60; + board_info.platform_data = &ts2020_config; + request_module("ts2020"); + client = i2c_new_device(i2c_adapter, &board_info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + dvb->i2c_client_tuner = client; + /* delegate signal strength measurement to tuner */ + dvb->fe[0]->ops.read_signal_strength = + dvb->fe[0]->ops.tuner_ops.get_rf_strength; - dvb->i2c_client_tuner = client; + /* attach SEC */ + a8293_pdata.dvb_frontend = dvb->fe[0]; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "a8293", I2C_NAME_SIZE); + board_info.addr = 0x08; + board_info.platform_data = &a8293_pdata; + request_module("a8293"); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_tuner->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_tuner); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_tuner->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_tuner); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; } + dvb->i2c_client_sec = client; break; + } case EM28178_BOARD_PCTV_292E: { struct i2c_adapter *adapter; @@ -1579,6 +1640,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); info.addr = 0x60; @@ -1639,6 +1701,7 @@ static int em28xx_dvb_init(struct em28xx *dev) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 0; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2146", I2C_NAME_SIZE); info.addr = 0x60; @@ -1727,7 +1790,6 @@ static int em28xx_dvb_fini(struct em28xx *dev) em28xx_info("Closing DVB extension\n"); dvb = dev->dvb; - client = dvb->i2c_client_tuner; em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); @@ -1744,7 +1806,15 @@ static int em28xx_dvb_fini(struct em28xx *dev) } } + /* remove I2C SEC */ + client = dvb->i2c_client_sec; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + /* remove I2C tuner */ + client = dvb->i2c_client_tuner; if (client) { module_put(client->dev.driver->owner); i2c_unregister_device(client); diff --git a/kernel/drivers/media/usb/em28xx/em28xx-vbi.c b/kernel/drivers/media/usb/em28xx/em28xx-vbi.c index 744e7ed74..e23c285b3 100644 --- a/kernel/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/kernel/drivers/media/usb/em28xx/em28xx-vbi.c @@ -31,10 +31,11 @@ /* ------------------------------------------------------------------ */ -static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int vbi_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned long size; @@ -61,7 +62,6 @@ static int vbi_buffer_prepare(struct vb2_buffer *vb) { struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_v4l2 *v4l2 = dev->v4l2; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); unsigned long size; size = v4l2->vbi_width * v4l2->vbi_height * 2; @@ -71,7 +71,7 @@ static int vbi_buffer_prepare(struct vb2_buffer *vb) __func__, vb2_plane_size(vb, 0), size); return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(vb, 0, size); return 0; } @@ -79,8 +79,10 @@ static int vbi_buffer_prepare(struct vb2_buffer *vb) static void vbi_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_buffer *buf = + container_of(vbuf, struct em28xx_buffer, vb); struct em28xx_dmaqueue *vbiq = &dev->vbiq; unsigned long flags = 0; diff --git a/kernel/drivers/media/usb/em28xx/em28xx-video.c b/kernel/drivers/media/usb/em28xx/em28xx-video.c index 14eba9c65..6a3cf342e 100644 --- a/kernel/drivers/media/usb/em28xx/em28xx-video.c +++ b/kernel/drivers/media/usb/em28xx/em28xx-video.c @@ -433,14 +433,14 @@ static inline void finish_buffer(struct em28xx *dev, { em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); - buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++; + buf->vb.sequence = dev->v4l2->field_count++; if (dev->v4l2->progressive) - buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + buf->vb.field = V4L2_FIELD_NONE; else - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.timestamp); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } /* @@ -839,7 +839,6 @@ static int get_ressource(enum v4l2_buf_type f_type) return EM28XX_RESOURCE_VBI; default: BUG(); - return 0; } } @@ -872,10 +871,11 @@ static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) Videobuf2 operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned long size; @@ -901,12 +901,12 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_v4l2 *v4l2 = dev->v4l2; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); unsigned long size; - em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); + em28xx_videodbg("%s, field=%d\n", __func__, vbuf->field); size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; @@ -915,7 +915,7 @@ buffer_prepare(struct vb2_buffer *vb) __func__, vb2_plane_size(vb, 0), size); return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(vb, 0, size); return 0; } @@ -925,6 +925,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_v4l2 *v4l2 = dev->v4l2; struct v4l2_frequency f; + struct v4l2_fh *owner; int rc = 0; em28xx_videodbg("%s\n", __func__); @@ -965,7 +966,8 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) /* Ask tuner to go to analog or radio mode */ memset(&f, 0, sizeof(f)); f.frequency = v4l2->frequency; - if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) + owner = (struct v4l2_fh *)vq->owner; + if (owner && owner->vdev->vfl_type == VFL_TYPE_RADIO) f.type = V4L2_TUNER_RADIO; else f.type = V4L2_TUNER_ANALOG_TV; @@ -996,7 +998,8 @@ static void em28xx_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&dev->slock, flags); if (dev->usb_ctl.vid_buf != NULL) { - vb2_buffer_done(&dev->usb_ctl.vid_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&dev->usb_ctl.vid_buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); dev->usb_ctl.vid_buf = NULL; } while (!list_empty(&vidq->active)) { @@ -1004,7 +1007,7 @@ static void em28xx_stop_streaming(struct vb2_queue *vq) buf = list_entry(vidq->active.next, struct em28xx_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -1027,7 +1030,8 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq) spin_lock_irqsave(&dev->slock, flags); if (dev->usb_ctl.vbi_buf != NULL) { - vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); dev->usb_ctl.vbi_buf = NULL; } while (!list_empty(&vbiq->active)) { @@ -1035,7 +1039,7 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq) buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&dev->slock, flags); } @@ -1043,8 +1047,10 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq) static void buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_buffer *buf = + container_of(vbuf, struct em28xx_buffer, vb); struct em28xx_dmaqueue *vidq = &dev->vidq; unsigned long flags = 0; diff --git a/kernel/drivers/media/usb/em28xx/em28xx.h b/kernel/drivers/media/usb/em28xx/em28xx.h index e6559c6f1..76bf8ba37 100644 --- a/kernel/drivers/media/usb/em28xx/em28xx.h +++ b/kernel/drivers/media/usb/em28xx/em28xx.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -264,7 +265,7 @@ struct em28xx_fmt { /* buffer for one video frame */ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; void *mem; diff --git a/kernel/drivers/media/usb/go7007/go7007-driver.c b/kernel/drivers/media/usb/go7007/go7007-driver.c index 95cffb771..ae1cfa792 100644 --- a/kernel/drivers/media/usb/go7007/go7007-driver.c +++ b/kernel/drivers/media/usb/go7007/go7007-driver.c @@ -386,10 +386,10 @@ start_error: */ static inline void store_byte(struct go7007_buffer *vb, u8 byte) { - if (vb && vb->vb.v4l2_planes[0].bytesused < GO7007_BUF_SIZE) { - u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); + if (vb && vb->vb.vb2_buf.planes[0].bytesused < GO7007_BUF_SIZE) { + u8 *ptr = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); - ptr[vb->vb.v4l2_planes[0].bytesused++] = byte; + ptr[vb->vb.vb2_buf.planes[0].bytesused++] = byte; } } @@ -401,7 +401,7 @@ static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *v .type = V4L2_EVENT_MOTION_DET, .u.motion_det = { .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, - .frame_sequence = vb->vb.v4l2_buf.sequence, + .frame_sequence = vb->vb.sequence, .region_mask = motion_regions, }, }; @@ -417,7 +417,7 @@ static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *v */ static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) { - u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + u32 *bytesused = &vb->vb.vb2_buf.planes[0].bytesused; unsigned motion[4] = { 0, 0, 0, 0 }; u32 motion_regions = 0; unsigned stride = (go->width + 7) >> 3; @@ -446,7 +446,7 @@ static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) */ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) { - u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; + u32 *bytesused; struct go7007_buffer *vb_tmp = NULL; if (vb == NULL) { @@ -458,24 +458,26 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf go->next_seq++; return vb; } + bytesused = &vb->vb.vb2_buf.planes[0].bytesused; - vb->vb.v4l2_buf.sequence = go->next_seq++; + vb->vb.sequence = go->next_seq++; if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE) go7007_motion_regions(go, vb); else go7007_set_motion_regions(go, vb, 0); - v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->vb.timestamp); vb_tmp = vb; spin_lock(&go->spinlock); list_del(&vb->list); if (list_empty(&go->vidq_active)) vb = NULL; else - vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); + vb = list_first_entry(&go->vidq_active, + struct go7007_buffer, list); go->active_buf = vb; spin_unlock(&go->spinlock); - vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&vb_tmp->vb.vb2_buf, VB2_BUF_STATE_DONE); return vb; } @@ -518,9 +520,10 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) } for (i = 0; i < length; ++i) { - if (vb && vb->vb.v4l2_planes[0].bytesused >= GO7007_BUF_SIZE - 3) { + if (vb && vb->vb.vb2_buf.planes[0].bytesused >= + GO7007_BUF_SIZE - 3) { v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); - vb->vb.v4l2_planes[0].bytesused = 0; + vb->vb.vb2_buf.planes[0].bytesused = 0; vb->frame_offset = 0; vb->modet_active = 0; vb = go->active_buf = NULL; @@ -600,7 +603,8 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) vb = frame_boundary(go, vb); go->seen_frame = buf[i] == frame_start_code; if (vb && go->seen_frame) - vb->frame_offset = vb->vb.v4l2_planes[0].bytesused; + vb->frame_offset = + vb->vb.vb2_buf.planes[0].bytesused; } /* Handle any special chunk types, or just write the * start code to the (potentially new) buffer */ diff --git a/kernel/drivers/media/usb/go7007/go7007-fw.c b/kernel/drivers/media/usb/go7007/go7007-fw.c index 5f4c9b9e8..60bf5f064 100644 --- a/kernel/drivers/media/usb/go7007/go7007-fw.c +++ b/kernel/drivers/media/usb/go7007/go7007-fw.c @@ -379,7 +379,7 @@ static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) buf = kzalloc(4096, GFP_KERNEL); if (buf == NULL) - return -1; + return -ENOMEM; for (i = 1; i < 32; ++i) { mjpeg_frame_header(go, buf + size, i); @@ -646,7 +646,7 @@ static int gen_mpeg1hdr_to_package(struct go7007 *go, buf = kzalloc(5120, GFP_KERNEL); if (buf == NULL) - return -1; + return -ENOMEM; framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); if (go->interlace_coding) @@ -832,7 +832,7 @@ static int gen_mpeg4hdr_to_package(struct go7007 *go, buf = kzalloc(5120, GFP_KERNEL); if (buf == NULL) - return -1; + return -ENOMEM; framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); i = 368; diff --git a/kernel/drivers/media/usb/go7007/go7007-priv.h b/kernel/drivers/media/usb/go7007/go7007-priv.h index 2251c3f99..745185eb0 100644 --- a/kernel/drivers/media/usb/go7007/go7007-priv.h +++ b/kernel/drivers/media/usb/go7007/go7007-priv.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include struct go7007; @@ -136,7 +136,7 @@ struct go7007_hpi_ops { #define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) struct go7007_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; unsigned int frame_offset; u32 modet_active; diff --git a/kernel/drivers/media/usb/go7007/go7007-usb.c b/kernel/drivers/media/usb/go7007/go7007-usb.c index 3f986e117..4857c467e 100644 --- a/kernel/drivers/media/usb/go7007/go7007-usb.c +++ b/kernel/drivers/media/usb/go7007/go7007-usb.c @@ -338,6 +338,7 @@ static const struct go7007_usb_board board_matrix_revolution = { }, }; +#if 0 static const struct go7007_usb_board board_lifeview_lr192 = { .flags = GO7007_USB_EZUSB, .main_info = { @@ -364,6 +365,7 @@ static const struct go7007_usb_board board_lifeview_lr192 = { }, }, }; +#endif static const struct go7007_usb_board board_endura = { .flags = 0, @@ -1096,8 +1098,10 @@ static int go7007_usb_probe(struct usb_interface *intf, case GO7007_BOARDID_LIFEVIEW_LR192: dev_err(&intf->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n"); return -ENODEV; +#if 0 name = "Lifeview TV Walker Ultra"; board = &board_lifeview_lr192; +#endif break; case GO7007_BOARDID_SENSORAY_2250: dev_info(&intf->dev, "Sensoray 2250 found\n"); diff --git a/kernel/drivers/media/usb/go7007/go7007-v4l2.c b/kernel/drivers/media/usb/go7007/go7007-v4l2.c index d6bf982ef..f3d187db9 100644 --- a/kernel/drivers/media/usb/go7007/go7007-v4l2.c +++ b/kernel/drivers/media/usb/go7007/go7007-v4l2.c @@ -52,7 +52,7 @@ static bool valid_pixelformat(u32 pixelformat) static u32 get_frame_type_flag(struct go7007_buffer *vb, int format) { - u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); + u8 *ptr = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); switch (format) { case V4L2_PIX_FMT_MJPEG: @@ -250,15 +250,17 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) go->encoder_v_offset = go->board_info->sensor_v_offset; if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - mbus_fmt.code = MEDIA_BUS_FMT_FIXED; - mbus_fmt.width = fmt ? fmt->fmt.pix.width : width; - mbus_fmt.height = height; + format.format.code = MEDIA_BUS_FMT_FIXED; + format.format.width = fmt ? fmt->fmt.pix.width : width; + format.format.height = height; go->encoder_h_halve = 0; go->encoder_v_halve = 0; go->encoder_subsample = 0; - call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); + call_all(&go->v4l2_dev, pad, set_fmt, NULL, &format); } else { if (width <= sensor_width / 4) { go->encoder_h_halve = 1; @@ -367,7 +369,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } static int go7007_queue_setup(struct vb2_queue *q, - const struct v4l2_format *fmt, + const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -384,8 +386,9 @@ static void go7007_buf_queue(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; struct go7007 *go = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); + container_of(vbuf, struct go7007_buffer, vb); unsigned long flags; spin_lock_irqsave(&go->spinlock, flags); @@ -395,12 +398,13 @@ static void go7007_buf_queue(struct vb2_buffer *vb) static int go7007_buf_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); + container_of(vbuf, struct go7007_buffer, vb); go7007_vb->modet_active = 0; go7007_vb->frame_offset = 0; - vb->v4l2_planes[0].bytesused = 0; + vb->planes[0].bytesused = 0; return 0; } @@ -408,15 +412,15 @@ static void go7007_buf_finish(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; struct go7007 *go = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct go7007_buffer *go7007_vb = - container_of(vb, struct go7007_buffer, vb); + container_of(vbuf, struct go7007_buffer, vb); u32 frame_type_flag = get_frame_type_flag(go7007_vb, go->format); - struct v4l2_buffer *buf = &vb->v4l2_buf; - buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_BFRAME | + vbuf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_BFRAME | V4L2_BUF_FLAG_PFRAME); - buf->flags |= frame_type_flag; - buf->field = V4L2_FIELD_NONE; + vbuf->flags |= frame_type_flag; + vbuf->field = V4L2_FIELD_NONE; } static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) diff --git a/kernel/drivers/media/usb/go7007/s2250-board.c b/kernel/drivers/media/usb/go7007/s2250-board.c index bb846680b..1466db150 100644 --- a/kernel/drivers/media/usb/go7007/s2250-board.c +++ b/kernel/drivers/media/usb/go7007/s2250-board.c @@ -405,12 +405,20 @@ static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static int s2250_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int s2250_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { + struct v4l2_mbus_framefmt *fmt = &format->format; struct s2250 *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + if (format->pad) + return -EINVAL; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + if (fmt->height < 640) { write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); write_reg_fp(client, 0x140, 0x060); @@ -479,13 +487,17 @@ static const struct v4l2_subdev_audio_ops s2250_audio_ops = { static const struct v4l2_subdev_video_ops s2250_video_ops = { .s_std = s2250_s_std, .s_routing = s2250_s_video_routing, - .s_mbus_fmt = s2250_s_mbus_fmt, +}; + +static const struct v4l2_subdev_pad_ops s2250_pad_ops = { + .set_fmt = s2250_set_fmt, }; static const struct v4l2_subdev_ops s2250_ops = { .core = &s2250_core_ops, .audio = &s2250_audio_ops, .video = &s2250_video_ops, + .pad = &s2250_pad_ops, }; /* --------------------------------------------------------------------------*/ @@ -617,7 +629,6 @@ MODULE_DEVICE_TABLE(i2c, s2250_id); static struct i2c_driver s2250_driver = { .driver = { - .owner = THIS_MODULE, .name = "s2250", }, .probe = s2250_probe, diff --git a/kernel/drivers/media/usb/gspca/benq.c b/kernel/drivers/media/usb/gspca/benq.c index 05f406dea..790baed33 100644 --- a/kernel/drivers/media/usb/gspca/benq.c +++ b/kernel/drivers/media/usb/gspca/benq.c @@ -236,8 +236,8 @@ static void sd_isoc_irq(struct urb *urb) } data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; - gspca_frame_add(gspca_dev, INTER_PACKET, - data, SD_PKT_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data, SD_PKT_SZ); } /* resubmit the URBs */ diff --git a/kernel/drivers/media/usb/gspca/gspca.c b/kernel/drivers/media/usb/gspca/gspca.c index e54cee856..af5cd8213 100644 --- a/kernel/drivers/media/usb/gspca/gspca.c +++ b/kernel/drivers/media/usb/gspca/gspca.c @@ -436,7 +436,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, } j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; - frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); + v4l2_get_timestamp(&frame->v4l2_buf.timestamp); frame->v4l2_buf.sequence = gspca_dev->sequence++; gspca_dev->image = frame->data; gspca_dev->image_len = 0; @@ -1909,7 +1909,7 @@ static ssize_t dev_read(struct file *file, char __user *data, } /* get a frame */ - timestamp = ktime_to_timeval(ktime_get()); + v4l2_get_timestamp(×tamp); timestamp.tv_sec--; n = 2; for (;;) { diff --git a/kernel/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/kernel/drivers/media/usb/gspca/m5602/m5602_s5k83a.c index 7cbc3a00b..bf6b21543 100644 --- a/kernel/drivers/media/usb/gspca/m5602/m5602_s5k83a.c +++ b/kernel/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -177,7 +177,7 @@ static int rotation_thread_function(void *data) __s32 vflip, hflip; set_current_state(TASK_INTERRUPTIBLE); - while (!schedule_timeout(100)) { + while (!schedule_timeout(msecs_to_jiffies(100))) { if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) break; diff --git a/kernel/drivers/media/usb/gspca/ov534.c b/kernel/drivers/media/usb/gspca/ov534.c index 146071b8e..bfff1d1c7 100644 --- a/kernel/drivers/media/usb/gspca/ov534.c +++ b/kernel/drivers/media/usb/gspca/ov534.c @@ -1491,8 +1491,13 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev, struct v4l2_fract *tpf = &cp->timeperframe; struct sd *sd = (struct sd *) gspca_dev; - /* Set requested framerate */ - sd->frame_rate = tpf->denominator / tpf->numerator; + if (tpf->numerator == 0 || tpf->denominator == 0) + /* Set default framerate */ + sd->frame_rate = 30; + else + /* Set requested framerate */ + sd->frame_rate = tpf->denominator / tpf->numerator; + if (gspca_dev->streaming) set_frame_rate(gspca_dev); diff --git a/kernel/drivers/media/usb/gspca/sn9c2028.c b/kernel/drivers/media/usb/gspca/sn9c2028.c index 39b6b2e02..4f2050a5e 100644 --- a/kernel/drivers/media/usb/gspca/sn9c2028.c +++ b/kernel/drivers/media/usb/gspca/sn9c2028.c @@ -33,6 +33,16 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ u8 sof_read; u16 model; + +#define MIN_AVG_LUM 8500 +#define MAX_AVG_LUM 10000 + int avg_lum; + u8 avg_lum_l; + + struct { /* autogain and gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; }; struct init_command { @@ -128,9 +138,9 @@ static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) status = -1; for (i = 0; i < 256 && status < 2; i++) status = sn9c2028_read1(gspca_dev); - if (status != 2) { + if (status < 0) { pr_err("long command status read error %d\n", status); - return (status < 0) ? status : -EIO; + return status; } memset(reading, 0, 4); @@ -178,6 +188,9 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x7005: PDEBUG(D_PROBE, "Genius Smart 300 camera"); break; + case 0x7003: + PDEBUG(D_PROBE, "Genius Videocam Live v2"); + break; case 0x8000: PDEBUG(D_PROBE, "DC31VC"); break; @@ -248,6 +261,78 @@ static int run_start_commands(struct gspca_dev *gspca_dev, return 0; } +static void set_gain(struct gspca_dev *gspca_dev, s32 g) +{ + struct sd *sd = (struct sd *) gspca_dev; + + struct init_command genius_vcam_live_gain_cmds[] = { + {{0x1d, 0x25, 0x10 /* This byte is gain */, + 0x20, 0xab, 0x00}, 0}, + }; + if (!gspca_dev->streaming) + return; + + switch (sd->model) { + case 0x7003: + genius_vcam_live_gain_cmds[0].instruction[2] = g; + run_start_commands(gspca_dev, genius_vcam_live_gain_cmds, + ARRAY_SIZE(genius_vcam_live_gain_cmds)); + break; + default: + break; + } +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* standalone gain control */ + case V4L2_CID_GAIN: + set_gain(gspca_dev, ctrl->val); + break; + /* autogain */ + case V4L2_CID_AUTOGAIN: + set_gain(gspca_dev, sd->gain->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + + switch (sd->model) { + case 0x7003: + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 20, 1, 0); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + break; + default: + break; + } + + return 0; +} static int start_spy_cam(struct gspca_dev *gspca_dev) { struct init_command spy_start_commands[] = { @@ -530,6 +615,119 @@ static int start_genius_cam(struct gspca_dev *gspca_dev) ARRAY_SIZE(genius_start_commands)); } +static int start_genius_videocam_live(struct gspca_dev *gspca_dev) +{ + int r; + struct sd *sd = (struct sd *) gspca_dev; + struct init_command genius_vcam_live_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 0}, + {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4}, + {{0x1c, 0x20, 0x00, 0x2d, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x01, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x02, 0x92, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4}, + {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}, + /* Camera should start to capture now. */ + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 0}, + {{0x1b, 0x32, 0x26, 0x00, 0x00, 0x00}, 0}, + {{0x1d, 0x25, 0x10, 0x20, 0xab, 0x00}, 0}, + }; + + r = run_start_commands(gspca_dev, genius_vcam_live_start_commands, + ARRAY_SIZE(genius_vcam_live_start_commands)); + if (r < 0) + return r; + + if (sd->gain) + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + + return r; +} + static int start_vivitar_cam(struct gspca_dev *gspca_dev) { struct init_command vivitar_start_commands[] = { @@ -623,6 +821,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case 0x7005: err_code = start_genius_cam(gspca_dev); break; + case 0x7003: + err_code = start_genius_videocam_live(gspca_dev); + break; case 0x8001: err_code = start_spy_cam(gspca_dev); break; @@ -640,6 +841,8 @@ static int sd_start(struct gspca_dev *gspca_dev) return -ENXIO; } + sd->avg_lum = -1; + return err_code; } @@ -659,6 +862,39 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PERR("Camera Stop command failed"); } +static void do_autogain(struct gspca_dev *gspca_dev, int avg_lum) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); + + if (avg_lum == -1) + return; + + if (avg_lum < MIN_AVG_LUM) { + if (cur_gain == sd->gain->maximum) + return; + cur_gain++; + v4l2_ctrl_s_ctrl(sd->gain, cur_gain); + } + if (avg_lum > MAX_AVG_LUM) { + if (cur_gain == sd->gain->minimum) + return; + cur_gain--; + v4l2_ctrl_s_ctrl(sd->gain, cur_gain); + } + +} + +static void sd_dqcallback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain)) + return; + + do_autogain(gspca_dev, sd->avg_lum); +} + /* Include sn9c2028 sof detection functions */ #include "sn9c2028.h" @@ -693,14 +929,17 @@ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, + .dq_callback = sd_dqcallback, .pkt_scan = sd_pkt_scan, }; /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */ + {USB_DEVICE(0x0458, 0x7003)}, /* Genius Videocam Live v2 */ /* The Genius Smart is untested. I can't find an owner ! */ /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */ {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */ diff --git a/kernel/drivers/media/usb/gspca/sn9c2028.h b/kernel/drivers/media/usb/gspca/sn9c2028.h index 8fd1d3e05..f85bc106b 100644 --- a/kernel/drivers/media/usb/gspca/sn9c2028.h +++ b/kernel/drivers/media/usb/gspca/sn9c2028.h @@ -21,8 +21,15 @@ * */ -static const unsigned char sn9c2028_sof_marker[5] = - { 0xff, 0xff, 0x00, 0xc4, 0xc4 }; +static const unsigned char sn9c2028_sof_marker[] = { + 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, + 0x00, + 0x00, /* seq */ + 0x00, + 0x00, + 0x00, /* avg luminance lower 8 bit */ + 0x00, /* avg luminance higher 8 bit */ +}; static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, unsigned char *m, int len) @@ -32,8 +39,13 @@ static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, /* Search for the SOF marker (fixed part) in the header */ for (i = 0; i < len; i++) { - if (m[i] == sn9c2028_sof_marker[sd->sof_read]) { + if ((m[i] == sn9c2028_sof_marker[sd->sof_read]) || + (sd->sof_read > 5)) { sd->sof_read++; + if (sd->sof_read == 11) + sd->avg_lum_l = m[i]; + if (sd->sof_read == 12) + sd->avg_lum = (m[i] << 8) + sd->avg_lum_l; if (sd->sof_read == sizeof(sn9c2028_sof_marker)) { PDEBUG(D_FRAM, "SOF found, bytes to analyze: %u." diff --git a/kernel/drivers/media/usb/gspca/sonixj.c b/kernel/drivers/media/usb/gspca/sonixj.c index c69b45d7c..fd1c8706d 100644 --- a/kernel/drivers/media/usb/gspca/sonixj.c +++ b/kernel/drivers/media/usb/gspca/sonixj.c @@ -1789,7 +1789,7 @@ static u32 expo_adjust(struct gspca_dev *gspca_dev, if (expo > 0x03ff) expo = 0x03ff; - if (expo < 0x0001) + if (expo < 0x0001) expo = 0x0001; gainOm[3] = expo >> 2; i2c_w8(gspca_dev, gainOm); diff --git a/kernel/drivers/media/usb/gspca/stk014.c b/kernel/drivers/media/usb/gspca/stk014.c index b0c70fea7..d324d001e 100644 --- a/kernel/drivers/media/usb/gspca/stk014.c +++ b/kernel/drivers/media/usb/gspca/stk014.c @@ -276,7 +276,7 @@ static int sd_start(struct gspca_dev *gspca_dev) gspca_dev->usb_err = ret; goto out; } - reg_r(gspca_dev, 0x0630); + reg_r(gspca_dev, 0x0630); rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ reg_r(gspca_dev, 0x0650); snd_val(gspca_dev, 0x000020, 0xffffffff); diff --git a/kernel/drivers/media/usb/gspca/topro.c b/kernel/drivers/media/usb/gspca/topro.c index c70ff406b..c028a5c24 100644 --- a/kernel/drivers/media/usb/gspca/topro.c +++ b/kernel/drivers/media/usb/gspca/topro.c @@ -4802,7 +4802,11 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev, struct v4l2_fract *tpf = &cp->timeperframe; int fr, i; - sd->framerate = tpf->denominator / tpf->numerator; + if (tpf->numerator == 0 || tpf->denominator == 0) + sd->framerate = 30; + else + sd->framerate = tpf->denominator / tpf->numerator; + if (gspca_dev->streaming) setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); diff --git a/kernel/drivers/media/usb/gspca/xirlink_cit.c b/kernel/drivers/media/usb/gspca/xirlink_cit.c index a41aa7817..d5ed9d36c 100644 --- a/kernel/drivers/media/usb/gspca/xirlink_cit.c +++ b/kernel/drivers/media/usb/gspca/xirlink_cit.c @@ -1772,7 +1772,8 @@ static int cit_start_model2(struct gspca_dev *gspca_dev) cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ sd->sof_len = 2; break; - /* case VIDEOSIZE_352x240: */ +#if 0 + case VIDEOSIZE_352x240: cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */ @@ -1780,6 +1781,7 @@ static int cit_start_model2(struct gspca_dev *gspca_dev) cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */ sd->sof_len = 2; break; +#endif case 352: /* 352x288 */ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */ @@ -1853,13 +1855,15 @@ static int cit_start_model2(struct gspca_dev *gspca_dev) cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */ clock_div = 8; break; - /* case VIDEOSIZE_352x240: */ +#if 0 + case VIDEOSIZE_352x240: /* This mode doesn't work as Windows programs it; changed to work */ cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */ cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */ cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ clock_div = 10; break; +#endif case 352: /* 352x288 */ cit_model2_Packet1(gspca_dev, 0x0014, 0x0003); cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */ @@ -1906,9 +1910,11 @@ static int cit_start_model2(struct gspca_dev *gspca_dev) case 320: /* 320x240 */ cit_model2_Packet1(gspca_dev, 0x0026, 0x0044); break; - /* case VIDEOSIZE_352x240: */ +#if 0 + case VIDEOSIZE_352x240: cit_model2_Packet1(gspca_dev, 0x0026, 0x0046); break; +#endif case 352: /* 352x288 */ cit_model2_Packet1(gspca_dev, 0x0026, 0x0048); break; diff --git a/kernel/drivers/media/usb/gspca/zc3xx.c b/kernel/drivers/media/usb/gspca/zc3xx.c index d3e1b6d8b..c5d8ee6fa 100644 --- a/kernel/drivers/media/usb/gspca/zc3xx.c +++ b/kernel/drivers/media/usb/gspca/zc3xx.c @@ -5942,23 +5942,23 @@ static void transfer_update(struct work_struct *work) reg07 = 0; good = 0; - for (;;) { + while (1) { msleep(100); /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */ mutex_lock(&gspca_dev->usb_lock); #ifdef CONFIG_PM if (gspca_dev->frozen) - goto err; + break; #endif if (!gspca_dev->present || !gspca_dev->streaming) - goto err; + break; /* Bit 0 of register 11 indicates FIFO overflow */ gspca_dev->usb_err = 0; reg11 = reg_r(gspca_dev, 0x0011); if (gspca_dev->usb_err) - goto err; + break; change = reg11 & 0x01; if (change) { /* overflow */ @@ -5987,12 +5987,12 @@ static void transfer_update(struct work_struct *work) gspca_dev->usb_err = 0; reg_w(gspca_dev, reg07, 0x0007); if (gspca_dev->usb_err) - goto err; + break; } mutex_unlock(&gspca_dev->usb_lock); } - return; -err: + + /* Something went wrong. Unlock and return */ mutex_unlock(&gspca_dev->usb_lock); } @@ -6360,7 +6360,7 @@ static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val <= jpeg_qual[i]) break; } - if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) + if (i == ARRAY_SIZE(jpeg_qual) || (i > 0 && i == qual && ctrl->val < jpeg_qual[i])) i--; /* With high quality settings we need max bandwidth */ diff --git a/kernel/drivers/media/usb/hackrf/hackrf.c b/kernel/drivers/media/usb/hackrf/hackrf.c index fd1fa412e..0fe5cb2c2 100644 --- a/kernel/drivers/media/usb/hackrf/hackrf.c +++ b/kernel/drivers/media/usb/hackrf/hackrf.c @@ -21,8 +21,18 @@ #include #include #include +#include #include +/* + * Used Avago MGA-81563 RF amplifier could be destroyed pretty easily with too + * strong signal or transmitting to bad antenna. + * Set RF gain control to 'grabbed' state by default for sure. + */ +static bool hackrf_enable_rf_gain_ctrl; +module_param_named(enable_rf_gain_ctrl, hackrf_enable_rf_gain_ctrl, bool, 0644); +MODULE_PARM_DESC(enable_rf_gain_ctrl, "enable RX/TX RF amplifier control (warn: could damage amplifier)"); + /* HackRF USB API commands (from HackRF Library) */ enum { CMD_SET_TRANSCEIVER_MODE = 0x01, @@ -31,8 +41,10 @@ enum { CMD_BOARD_ID_READ = 0x0e, CMD_VERSION_STRING_READ = 0x0f, CMD_SET_FREQ = 0x10, + CMD_AMP_ENABLE = 0x11, CMD_SET_LNA_GAIN = 0x13, CMD_SET_VGA_GAIN = 0x14, + CMD_SET_TXVGA_GAIN = 0x15, }; /* @@ -43,10 +55,10 @@ enum { #define MAX_BULK_BUFS (6) #define BULK_BUFFER_SIZE (128 * 512) -static const struct v4l2_frequency_band bands_adc[] = { +static const struct v4l2_frequency_band bands_adc_dac[] = { { .tuner = 0, - .type = V4L2_TUNER_ADC, + .type = V4L2_TUNER_SDR, .index = 0, .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, .rangelow = 200000, @@ -54,7 +66,7 @@ static const struct v4l2_frequency_band bands_adc[] = { }, }; -static const struct v4l2_frequency_band bands_rf[] = { +static const struct v4l2_frequency_band bands_rx_tx[] = { { .tuner = 1, .type = V4L2_TUNER_RF, @@ -67,7 +79,6 @@ static const struct v4l2_frequency_band bands_rf[] = { /* stream formats */ struct hackrf_format { - char *name; u32 pixelformat; u32 buffersize; }; @@ -75,7 +86,6 @@ struct hackrf_format { /* format descriptions for capture and preview */ static struct hackrf_format formats[] = { { - .name = "Complex S8", .pixelformat = V4L2_SDR_FMT_CS8, .buffersize = BULK_BUFFER_SIZE, }, @@ -84,28 +94,44 @@ static struct hackrf_format formats[] = { static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); /* intermediate buffers with raw data from the USB device */ -struct hackrf_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ +struct hackrf_buffer { + struct vb2_v4l2_buffer vb; struct list_head list; }; struct hackrf_dev { -#define POWER_ON (1 << 1) -#define URB_BUF (1 << 2) -#define USB_STATE_URB_BUF (1 << 3) +#define USB_STATE_URB_BUF 1 /* XXX: set manually */ +#define RX_ON 4 +#define TX_ON 5 +#define RX_ADC_FREQUENCY 11 +#define TX_DAC_FREQUENCY 12 +#define RX_BANDWIDTH 13 +#define TX_BANDWIDTH 14 +#define RX_RF_FREQUENCY 15 +#define TX_RF_FREQUENCY 16 +#define RX_RF_GAIN 17 +#define TX_RF_GAIN 18 +#define RX_IF_GAIN 19 +#define RX_LNA_GAIN 20 +#define TX_LNA_GAIN 21 unsigned long flags; + struct usb_interface *intf; struct device *dev; struct usb_device *udev; - struct video_device vdev; + struct video_device rx_vdev; + struct video_device tx_vdev; struct v4l2_device v4l2_dev; /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + struct vb2_queue rx_vb2_queue; + struct vb2_queue tx_vb2_queue; + struct list_head rx_buffer_list; + struct list_head tx_buffer_list; + spinlock_t buffer_list_lock; /* Protects buffer_list */ unsigned sequence; /* Buffer sequence counter */ unsigned int vb_full; /* vb is full and packets dropped */ + unsigned int vb_empty; /* vb is empty and packets dropped */ /* Note if taking both locks v4l2_lock must always be locked first! */ struct mutex v4l2_lock; /* Protects everything else */ @@ -125,16 +151,24 @@ struct hackrf_dev { /* Current configuration */ unsigned int f_adc; - unsigned int f_rf; + unsigned int f_dac; + unsigned int f_rx; + unsigned int f_tx; u32 pixelformat; u32 buffersize; /* Controls */ - struct v4l2_ctrl_handler hdl; - struct v4l2_ctrl *bandwidth_auto; - struct v4l2_ctrl *bandwidth; - struct v4l2_ctrl *lna_gain; - struct v4l2_ctrl *if_gain; + struct v4l2_ctrl_handler rx_ctrl_handler; + struct v4l2_ctrl *rx_bandwidth_auto; + struct v4l2_ctrl *rx_bandwidth; + struct v4l2_ctrl *rx_rf_gain; + struct v4l2_ctrl *rx_lna_gain; + struct v4l2_ctrl *rx_if_gain; + struct v4l2_ctrl_handler tx_ctrl_handler; + struct v4l2_ctrl *tx_bandwidth_auto; + struct v4l2_ctrl *tx_bandwidth; + struct v4l2_ctrl *tx_rf_gain; + struct v4l2_ctrl *tx_lna_gain; /* Sample rate calc */ unsigned long jiffies_next; @@ -164,6 +198,7 @@ static int hackrf_ctrl_msg(struct hackrf_dev *dev, u8 request, u16 value, switch (request) { case CMD_SET_TRANSCEIVER_MODE: case CMD_SET_FREQ: + case CMD_AMP_ENABLE: case CMD_SAMPLE_RATE_SET: case CMD_BASEBAND_FILTER_BANDWIDTH_SET: pipe = usb_sndctrlpipe(dev->udev, 0); @@ -173,6 +208,7 @@ static int hackrf_ctrl_msg(struct hackrf_dev *dev, u8 request, u16 value, case CMD_VERSION_STRING_READ: case CMD_SET_LNA_GAIN: case CMD_SET_VGA_GAIN: + case CMD_SET_TXVGA_GAIN: pipe = usb_rcvctrlpipe(dev->udev, 0); requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); break; @@ -205,25 +241,227 @@ err: return ret; } +static int hackrf_set_params(struct hackrf_dev *dev) +{ + struct usb_interface *intf = dev->intf; + int ret, i; + u8 buf[8], u8tmp; + unsigned int uitmp, uitmp1, uitmp2; + const bool rx = test_bit(RX_ON, &dev->flags); + const bool tx = test_bit(TX_ON, &dev->flags); + static const struct { + u32 freq; + } bandwidth_lut[] = { + { 1750000}, /* 1.75 MHz */ + { 2500000}, /* 2.5 MHz */ + { 3500000}, /* 3.5 MHz */ + { 5000000}, /* 5 MHz */ + { 5500000}, /* 5.5 MHz */ + { 6000000}, /* 6 MHz */ + { 7000000}, /* 7 MHz */ + { 8000000}, /* 8 MHz */ + { 9000000}, /* 9 MHz */ + {10000000}, /* 10 MHz */ + {12000000}, /* 12 MHz */ + {14000000}, /* 14 MHz */ + {15000000}, /* 15 MHz */ + {20000000}, /* 20 MHz */ + {24000000}, /* 24 MHz */ + {28000000}, /* 28 MHz */ + }; + + if (!rx && !tx) { + dev_dbg(&intf->dev, "device is sleeping\n"); + return 0; + } + + /* ADC / DAC frequency */ + if (rx && test_and_clear_bit(RX_ADC_FREQUENCY, &dev->flags)) { + dev_dbg(&intf->dev, "RX ADC frequency=%u Hz\n", dev->f_adc); + uitmp1 = dev->f_adc; + uitmp2 = 1; + set_bit(TX_DAC_FREQUENCY, &dev->flags); + } else if (tx && test_and_clear_bit(TX_DAC_FREQUENCY, &dev->flags)) { + dev_dbg(&intf->dev, "TX DAC frequency=%u Hz\n", dev->f_dac); + uitmp1 = dev->f_dac; + uitmp2 = 1; + set_bit(RX_ADC_FREQUENCY, &dev->flags); + } else { + uitmp1 = uitmp2 = 0; + } + if (uitmp1 || uitmp2) { + buf[0] = (uitmp1 >> 0) & 0xff; + buf[1] = (uitmp1 >> 8) & 0xff; + buf[2] = (uitmp1 >> 16) & 0xff; + buf[3] = (uitmp1 >> 24) & 0xff; + buf[4] = (uitmp2 >> 0) & 0xff; + buf[5] = (uitmp2 >> 8) & 0xff; + buf[6] = (uitmp2 >> 16) & 0xff; + buf[7] = (uitmp2 >> 24) & 0xff; + ret = hackrf_ctrl_msg(dev, CMD_SAMPLE_RATE_SET, 0, 0, buf, 8); + if (ret) + goto err; + } + + /* bandwidth */ + if (rx && test_and_clear_bit(RX_BANDWIDTH, &dev->flags)) { + if (dev->rx_bandwidth_auto->val == true) + uitmp = dev->f_adc; + else + uitmp = dev->rx_bandwidth->val; + + for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { + if (uitmp <= bandwidth_lut[i].freq) { + uitmp = bandwidth_lut[i].freq; + break; + } + } + dev->rx_bandwidth->val = uitmp; + dev->rx_bandwidth->cur.val = uitmp; + dev_dbg(&intf->dev, "RX bandwidth selected=%u\n", uitmp); + set_bit(TX_BANDWIDTH, &dev->flags); + } else if (tx && test_and_clear_bit(TX_BANDWIDTH, &dev->flags)) { + if (dev->tx_bandwidth_auto->val == true) + uitmp = dev->f_dac; + else + uitmp = dev->tx_bandwidth->val; + + for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { + if (uitmp <= bandwidth_lut[i].freq) { + uitmp = bandwidth_lut[i].freq; + break; + } + } + dev->tx_bandwidth->val = uitmp; + dev->tx_bandwidth->cur.val = uitmp; + dev_dbg(&intf->dev, "TX bandwidth selected=%u\n", uitmp); + set_bit(RX_BANDWIDTH, &dev->flags); + } else { + uitmp = 0; + } + if (uitmp) { + uitmp1 = uitmp2 = 0; + uitmp1 |= ((uitmp >> 0) & 0xff) << 0; + uitmp1 |= ((uitmp >> 8) & 0xff) << 8; + uitmp2 |= ((uitmp >> 16) & 0xff) << 0; + uitmp2 |= ((uitmp >> 24) & 0xff) << 8; + ret = hackrf_ctrl_msg(dev, CMD_BASEBAND_FILTER_BANDWIDTH_SET, + uitmp1, uitmp2, NULL, 0); + if (ret) + goto err; + } + + /* RX / TX RF frequency */ + if (rx && test_and_clear_bit(RX_RF_FREQUENCY, &dev->flags)) { + dev_dbg(&intf->dev, "RX RF frequency=%u Hz\n", dev->f_rx); + uitmp1 = dev->f_rx / 1000000; + uitmp2 = dev->f_rx % 1000000; + set_bit(TX_RF_FREQUENCY, &dev->flags); + } else if (tx && test_and_clear_bit(TX_RF_FREQUENCY, &dev->flags)) { + dev_dbg(&intf->dev, "TX RF frequency=%u Hz\n", dev->f_tx); + uitmp1 = dev->f_tx / 1000000; + uitmp2 = dev->f_tx % 1000000; + set_bit(RX_RF_FREQUENCY, &dev->flags); + } else { + uitmp1 = uitmp2 = 0; + } + if (uitmp1 || uitmp2) { + buf[0] = (uitmp1 >> 0) & 0xff; + buf[1] = (uitmp1 >> 8) & 0xff; + buf[2] = (uitmp1 >> 16) & 0xff; + buf[3] = (uitmp1 >> 24) & 0xff; + buf[4] = (uitmp2 >> 0) & 0xff; + buf[5] = (uitmp2 >> 8) & 0xff; + buf[6] = (uitmp2 >> 16) & 0xff; + buf[7] = (uitmp2 >> 24) & 0xff; + ret = hackrf_ctrl_msg(dev, CMD_SET_FREQ, 0, 0, buf, 8); + if (ret) + goto err; + } + + /* RX RF gain */ + if (rx && test_and_clear_bit(RX_RF_GAIN, &dev->flags)) { + dev_dbg(&intf->dev, "RX RF gain val=%d->%d\n", + dev->rx_rf_gain->cur.val, dev->rx_rf_gain->val); + + u8tmp = (dev->rx_rf_gain->val) ? 1 : 0; + ret = hackrf_ctrl_msg(dev, CMD_AMP_ENABLE, u8tmp, 0, NULL, 0); + if (ret) + goto err; + set_bit(TX_RF_GAIN, &dev->flags); + } + + /* TX RF gain */ + if (tx && test_and_clear_bit(TX_RF_GAIN, &dev->flags)) { + dev_dbg(&intf->dev, "TX RF gain val=%d->%d\n", + dev->tx_rf_gain->cur.val, dev->tx_rf_gain->val); + + u8tmp = (dev->tx_rf_gain->val) ? 1 : 0; + ret = hackrf_ctrl_msg(dev, CMD_AMP_ENABLE, u8tmp, 0, NULL, 0); + if (ret) + goto err; + set_bit(RX_RF_GAIN, &dev->flags); + } + + /* RX LNA gain */ + if (rx && test_and_clear_bit(RX_LNA_GAIN, &dev->flags)) { + dev_dbg(dev->dev, "RX LNA gain val=%d->%d\n", + dev->rx_lna_gain->cur.val, dev->rx_lna_gain->val); + + ret = hackrf_ctrl_msg(dev, CMD_SET_LNA_GAIN, 0, + dev->rx_lna_gain->val, &u8tmp, 1); + if (ret) + goto err; + } + + /* RX IF gain */ + if (rx && test_and_clear_bit(RX_IF_GAIN, &dev->flags)) { + dev_dbg(&intf->dev, "IF gain val=%d->%d\n", + dev->rx_if_gain->cur.val, dev->rx_if_gain->val); + + ret = hackrf_ctrl_msg(dev, CMD_SET_VGA_GAIN, 0, + dev->rx_if_gain->val, &u8tmp, 1); + if (ret) + goto err; + } + + /* TX LNA gain */ + if (tx && test_and_clear_bit(TX_LNA_GAIN, &dev->flags)) { + dev_dbg(&intf->dev, "TX LNA gain val=%d->%d\n", + dev->tx_lna_gain->cur.val, dev->tx_lna_gain->val); + + ret = hackrf_ctrl_msg(dev, CMD_SET_TXVGA_GAIN, 0, + dev->tx_lna_gain->val, &u8tmp, 1); + if (ret) + goto err; + } + + return 0; +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); + return ret; +} + /* Private functions */ -static struct hackrf_frame_buf *hackrf_get_next_fill_buf(struct hackrf_dev *dev) +static struct hackrf_buffer *hackrf_get_next_buffer(struct hackrf_dev *dev, + struct list_head *buffer_list) { unsigned long flags; - struct hackrf_frame_buf *buf = NULL; + struct hackrf_buffer *buffer = NULL; - spin_lock_irqsave(&dev->queued_bufs_lock, flags); - if (list_empty(&dev->queued_bufs)) + spin_lock_irqsave(&dev->buffer_list_lock, flags); + if (list_empty(buffer_list)) goto leave; - buf = list_entry(dev->queued_bufs.next, struct hackrf_frame_buf, list); - list_del(&buf->list); + buffer = list_entry(buffer_list->next, struct hackrf_buffer, list); + list_del(&buffer->list); leave: - spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); - return buf; + spin_unlock_irqrestore(&dev->buffer_list_lock, flags); + return buffer; } -static unsigned int hackrf_convert_stream(struct hackrf_dev *dev, - void *dst, void *src, unsigned int src_len) +static void hackrf_copy_stream(struct hackrf_dev *dev, void *dst, void *src, + unsigned int src_len) { memcpy(dst, src, src_len); @@ -243,22 +481,21 @@ static unsigned int hackrf_convert_stream(struct hackrf_dev *dev, /* total number of samples */ dev->sample += src_len / 2; - - return src_len; } /* * This gets called for the bulk stream pipe. This is done in interrupt * time, so it has to be fast, not crash, and not stall. Neat. */ -static void hackrf_urb_complete(struct urb *urb) +static void hackrf_urb_complete_in(struct urb *urb) { struct hackrf_dev *dev = urb->context; - struct hackrf_frame_buf *fbuf; + struct usb_interface *intf = dev->intf; + struct hackrf_buffer *buffer; + unsigned int len; - dev_dbg_ratelimited(dev->dev, "status=%d length=%d/%d errors=%d\n", - urb->status, urb->actual_length, - urb->transfer_buffer_length, urb->error_count); + dev_dbg_ratelimited(&intf->dev, "status=%d length=%u/%u\n", urb->status, + urb->actual_length, urb->transfer_buffer_length); switch (urb->status) { case 0: /* success */ @@ -269,33 +506,74 @@ static void hackrf_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_err_ratelimited(dev->dev, "URB failed %d\n", urb->status); - break; + dev_err_ratelimited(&intf->dev, "URB failed %d\n", urb->status); + goto exit_usb_submit_urb; } - if (likely(urb->actual_length > 0)) { - void *ptr; - unsigned int len; - /* get free framebuffer */ - fbuf = hackrf_get_next_fill_buf(dev); - if (unlikely(fbuf == NULL)) { - dev->vb_full++; - dev_notice_ratelimited(dev->dev, - "videobuf is full, %d packets dropped\n", - dev->vb_full); - goto skip; - } + /* get buffer to write */ + buffer = hackrf_get_next_buffer(dev, &dev->rx_buffer_list); + if (unlikely(buffer == NULL)) { + dev->vb_full++; + dev_notice_ratelimited(&intf->dev, + "buffer is full - %u packets dropped\n", + dev->vb_full); + goto exit_usb_submit_urb; + } - /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); - len = hackrf_convert_stream(dev, ptr, urb->transfer_buffer, - urb->actual_length); - vb2_set_plane_payload(&fbuf->vb, 0, len); - v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = dev->sequence++; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + len = min_t(unsigned long, vb2_plane_size(&buffer->vb.vb2_buf, 0), + urb->actual_length); + hackrf_copy_stream(dev, vb2_plane_vaddr(&buffer->vb.vb2_buf, 0), + urb->transfer_buffer, len); + vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, len); + buffer->vb.sequence = dev->sequence++; + v4l2_get_timestamp(&buffer->vb.timestamp); + vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_DONE); +exit_usb_submit_urb: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void hackrf_urb_complete_out(struct urb *urb) +{ + struct hackrf_dev *dev = urb->context; + struct usb_interface *intf = dev->intf; + struct hackrf_buffer *buffer; + unsigned int len; + + dev_dbg_ratelimited(&intf->dev, "status=%d length=%u/%u\n", urb->status, + urb->actual_length, urb->transfer_buffer_length); + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dev_err_ratelimited(&intf->dev, "URB failed %d\n", urb->status); } -skip: + + /* get buffer to read */ + buffer = hackrf_get_next_buffer(dev, &dev->tx_buffer_list); + if (unlikely(buffer == NULL)) { + dev->vb_empty++; + dev_notice_ratelimited(&intf->dev, + "buffer is empty - %u packets dropped\n", + dev->vb_empty); + urb->actual_length = 0; + goto exit_usb_submit_urb; + } + + len = min_t(unsigned long, urb->transfer_buffer_length, + vb2_get_plane_payload(&buffer->vb.vb2_buf, 0)); + hackrf_copy_stream(dev, urb->transfer_buffer, + vb2_plane_vaddr(&buffer->vb.vb2_buf, 0), len); + urb->actual_length = len; + buffer->vb.sequence = dev->sequence++; + v4l2_get_timestamp(&buffer->vb.timestamp); + vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_DONE); +exit_usb_submit_urb: usb_submit_urb(urb, GFP_ATOMIC); } @@ -394,9 +672,19 @@ static int hackrf_free_urbs(struct hackrf_dev *dev) return 0; } -static int hackrf_alloc_urbs(struct hackrf_dev *dev) +static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv) { int i, j; + unsigned int pipe; + usb_complete_t complete; + + if (rcv) { + pipe = usb_rcvbulkpipe(dev->udev, 0x81); + complete = &hackrf_urb_complete_in; + } else { + pipe = usb_sndbulkpipe(dev->udev, 0x02); + complete = &hackrf_urb_complete_out; + } /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { @@ -410,10 +698,10 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev) } usb_fill_bulk_urb(dev->urb_list[i], dev->udev, - usb_rcvbulkpipe(dev->udev, 0x81), + pipe, dev->buf_list[i], BULK_BUFFER_SIZE, - hackrf_urb_complete, dev); + complete, dev); dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; dev->urb_list[i]->transfer_dma = dev->dma_addr[i]; @@ -423,25 +711,6 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev) return 0; } -/* Must be called with vb_queue_lock hold */ -static void hackrf_cleanup_queued_bufs(struct hackrf_dev *dev) -{ - unsigned long flags; - - dev_dbg(dev->dev, "\n"); - - spin_lock_irqsave(&dev->queued_bufs_lock, flags); - while (!list_empty(&dev->queued_bufs)) { - struct hackrf_frame_buf *buf; - - buf = list_entry(dev->queued_bufs.next, - struct hackrf_frame_buf, list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); -} - /* The user yanked out the cable... */ static void hackrf_disconnect(struct usb_interface *intf) { @@ -455,7 +724,8 @@ static void hackrf_disconnect(struct usb_interface *intf) /* No need to keep the urbs around after disconnection */ dev->udev = NULL; v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(&dev->vdev); + video_unregister_device(&dev->tx_vdev); + video_unregister_device(&dev->rx_vdev); mutex_unlock(&dev->v4l2_lock); mutex_unlock(&dev->vb_queue_lock); @@ -463,8 +733,33 @@ static void hackrf_disconnect(struct usb_interface *intf) } /* Videobuf2 operations */ +static void hackrf_return_all_buffers(struct vb2_queue *vq, + enum vb2_buffer_state state) +{ + struct hackrf_dev *dev = vb2_get_drv_priv(vq); + struct usb_interface *intf = dev->intf; + struct hackrf_buffer *buffer, *node; + struct list_head *buffer_list; + unsigned long flags; + + dev_dbg(&intf->dev, "\n"); + + if (vq->type == V4L2_BUF_TYPE_SDR_CAPTURE) + buffer_list = &dev->rx_buffer_list; + else + buffer_list = &dev->tx_buffer_list; + + spin_lock_irqsave(&dev->buffer_list_lock, flags); + list_for_each_entry_safe(buffer, node, buffer_list, list) { + dev_dbg(&intf->dev, "list_for_each_entry_safe\n"); + vb2_buffer_done(&buffer->vb.vb2_buf, state); + list_del(&buffer->list); + } + spin_unlock_irqrestore(&dev->buffer_list_lock, flags); +} + static int hackrf_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct hackrf_dev *dev = vb2_get_drv_priv(vq); @@ -483,37 +778,62 @@ static int hackrf_queue_setup(struct vb2_queue *vq, static void hackrf_buf_queue(struct vb2_buffer *vb) { - struct hackrf_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - struct hackrf_frame_buf *buf = - container_of(vb, struct hackrf_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct hackrf_dev *dev = vb2_get_drv_priv(vq); + struct hackrf_buffer *buffer = container_of(vbuf, struct hackrf_buffer, vb); + struct list_head *buffer_list; unsigned long flags; - spin_lock_irqsave(&dev->queued_bufs_lock, flags); - list_add_tail(&buf->list, &dev->queued_bufs); - spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); + dev_dbg_ratelimited(&dev->intf->dev, "\n"); + + if (vq->type == V4L2_BUF_TYPE_SDR_CAPTURE) + buffer_list = &dev->rx_buffer_list; + else + buffer_list = &dev->tx_buffer_list; + + spin_lock_irqsave(&dev->buffer_list_lock, flags); + list_add_tail(&buffer->list, buffer_list); + spin_unlock_irqrestore(&dev->buffer_list_lock, flags); } static int hackrf_start_streaming(struct vb2_queue *vq, unsigned int count) { struct hackrf_dev *dev = vb2_get_drv_priv(vq); + struct usb_interface *intf = dev->intf; int ret; + unsigned int mode; - dev_dbg(dev->dev, "\n"); - - if (!dev->udev) - return -ENODEV; + dev_dbg(&intf->dev, "count=%i\n", count); mutex_lock(&dev->v4l2_lock); - dev->sequence = 0; + /* Allow only RX or TX, not both same time */ + if (vq->type == V4L2_BUF_TYPE_SDR_CAPTURE) { + if (test_bit(TX_ON, &dev->flags)) { + ret = -EBUSY; + goto err_hackrf_return_all_buffers; + } + + mode = 1; + set_bit(RX_ON, &dev->flags); + } else { + if (test_bit(RX_ON, &dev->flags)) { + ret = -EBUSY; + goto err_hackrf_return_all_buffers; + } + + mode = 2; + set_bit(TX_ON, &dev->flags); + } - set_bit(POWER_ON, &dev->flags); + dev->sequence = 0; ret = hackrf_alloc_stream_bufs(dev); if (ret) goto err; - ret = hackrf_alloc_urbs(dev); + ret = hackrf_alloc_urbs(dev, (mode == 1)); if (ret) goto err; @@ -521,39 +841,37 @@ static int hackrf_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + ret = hackrf_set_params(dev); + if (ret) + goto err; + /* start hardware streaming */ - ret = hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, 1, 0, NULL, 0); + ret = hackrf_ctrl_msg(dev, CMD_SET_TRANSCEIVER_MODE, mode, 0, NULL, 0); if (ret) goto err; - goto exit_mutex_unlock; + mutex_unlock(&dev->v4l2_lock); + + return 0; err: hackrf_kill_urbs(dev); hackrf_free_urbs(dev); hackrf_free_stream_bufs(dev); - clear_bit(POWER_ON, &dev->flags); - - /* return all queued buffers to vb2 */ - { - struct hackrf_frame_buf *buf, *tmp; - - list_for_each_entry_safe(buf, tmp, &dev->queued_bufs, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); - } - } - -exit_mutex_unlock: + clear_bit(RX_ON, &dev->flags); + clear_bit(TX_ON, &dev->flags); +err_hackrf_return_all_buffers: + hackrf_return_all_buffers(vq, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); - + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } static void hackrf_stop_streaming(struct vb2_queue *vq) { struct hackrf_dev *dev = vb2_get_drv_priv(vq); + struct usb_interface *intf = dev->intf; - dev_dbg(dev->dev, "\n"); + dev_dbg(&intf->dev, "\n"); mutex_lock(&dev->v4l2_lock); @@ -564,9 +882,12 @@ static void hackrf_stop_streaming(struct vb2_queue *vq) hackrf_free_urbs(dev); hackrf_free_stream_bufs(dev); - hackrf_cleanup_queued_bufs(dev); + hackrf_return_all_buffers(vq, VB2_BUF_STATE_ERROR); - clear_bit(POWER_ON, &dev->flags); + if (vq->type == V4L2_BUF_TYPE_SDR_CAPTURE) + clear_bit(RX_ON, &dev->flags); + else + clear_bit(TX_ON, &dev->flags); mutex_unlock(&dev->v4l2_lock); } @@ -584,29 +905,46 @@ static int hackrf_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct hackrf_dev *dev = video_drvdata(file); + struct usb_interface *intf = dev->intf; + struct video_device *vdev = video_devdata(file); - dev_dbg(dev->dev, "\n"); + dev_dbg(&intf->dev, "\n"); + + if (vdev->vfl_dir == VFL_DIR_RX) + cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + else + cap->device_caps = V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + + cap->capabilities = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, dev->vdev.name, sizeof(cap->card)); + strlcpy(cap->card, dev->rx_vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int hackrf_s_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int hackrf_s_fmt_sdr(struct file *file, void *priv, + struct v4l2_format *f) { struct hackrf_dev *dev = video_drvdata(file); - struct vb2_queue *q = &dev->vb_queue; + struct video_device *vdev = video_devdata(file); + struct vb2_queue *q; int i; dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", (char *)&f->fmt.sdr.pixelformat); + if (vdev->vfl_dir == VFL_DIR_RX) + q = &dev->rx_vb2_queue; + else + q = &dev->tx_vb2_queue; + if (vb2_is_busy(q)) return -EBUSY; @@ -628,8 +966,8 @@ static int hackrf_s_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int hackrf_g_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int hackrf_g_fmt_sdr(struct file *file, void *priv, + struct v4l2_format *f) { struct hackrf_dev *dev = video_drvdata(file); @@ -643,8 +981,8 @@ static int hackrf_g_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int hackrf_try_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int hackrf_try_fmt_sdr(struct file *file, void *priv, + struct v4l2_format *f) { struct hackrf_dev *dev = video_drvdata(file); int i; @@ -666,8 +1004,8 @@ static int hackrf_try_fmt_sdr_cap(struct file *file, void *priv, return 0; } -static int hackrf_enum_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int hackrf_enum_fmt_sdr(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct hackrf_dev *dev = video_drvdata(file); @@ -676,7 +1014,6 @@ static int hackrf_enum_fmt_sdr_cap(struct file *file, void *priv, if (f->index >= NUM_FORMATS) return -EINVAL; - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); f->pixelformat = formats[f->index].pixelformat; return 0; @@ -709,17 +1046,56 @@ static int hackrf_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) if (v->index == 0) { strlcpy(v->name, "HackRF ADC", sizeof(v->name)); - v->type = V4L2_TUNER_ADC; + v->type = V4L2_TUNER_SDR; v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = bands_adc[0].rangelow; - v->rangehigh = bands_adc[0].rangehigh; + v->rangelow = bands_adc_dac[0].rangelow; + v->rangehigh = bands_adc_dac[0].rangehigh; ret = 0; } else if (v->index == 1) { strlcpy(v->name, "HackRF RF", sizeof(v->name)); v->type = V4L2_TUNER_RF; v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; - v->rangelow = bands_rf[0].rangelow; - v->rangehigh = bands_rf[0].rangehigh; + v->rangelow = bands_rx_tx[0].rangelow; + v->rangehigh = bands_rx_tx[0].rangehigh; + ret = 0; + } else { + ret = -EINVAL; + } + + return ret; +} + +static int hackrf_s_modulator(struct file *file, void *fh, + const struct v4l2_modulator *a) +{ + struct hackrf_dev *dev = video_drvdata(file); + + dev_dbg(dev->dev, "index=%d\n", a->index); + + return a->index > 1 ? -EINVAL : 0; +} + +static int hackrf_g_modulator(struct file *file, void *fh, + struct v4l2_modulator *a) +{ + struct hackrf_dev *dev = video_drvdata(file); + int ret; + + dev_dbg(dev->dev, "index=%d\n", a->index); + + if (a->index == 0) { + strlcpy(a->name, "HackRF DAC", sizeof(a->name)); + a->type = V4L2_TUNER_SDR; + a->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + a->rangelow = bands_adc_dac[0].rangelow; + a->rangehigh = bands_adc_dac[0].rangehigh; + ret = 0; + } else if (a->index == 1) { + strlcpy(a->name, "HackRF RF", sizeof(a->name)); + a->type = V4L2_TUNER_RF; + a->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + a->rangelow = bands_rx_tx[0].rangelow; + a->rangehigh = bands_rx_tx[0].rangehigh; ret = 0; } else { ret = -EINVAL; @@ -732,47 +1108,46 @@ static int hackrf_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { struct hackrf_dev *dev = video_drvdata(file); + struct usb_interface *intf = dev->intf; + struct video_device *vdev = video_devdata(file); int ret; - unsigned int upper, lower; - u8 buf[8]; + unsigned int uitmp; - dev_dbg(dev->dev, "tuner=%d type=%d frequency=%u\n", + dev_dbg(&intf->dev, "tuner=%d type=%d frequency=%u\n", f->tuner, f->type, f->frequency); if (f->tuner == 0) { - dev->f_adc = clamp_t(unsigned int, f->frequency, - bands_adc[0].rangelow, bands_adc[0].rangehigh); - dev_dbg(dev->dev, "ADC frequency=%u Hz\n", dev->f_adc); - upper = dev->f_adc; - lower = 1; - buf[0] = (upper >> 0) & 0xff; - buf[1] = (upper >> 8) & 0xff; - buf[2] = (upper >> 16) & 0xff; - buf[3] = (upper >> 24) & 0xff; - buf[4] = (lower >> 0) & 0xff; - buf[5] = (lower >> 8) & 0xff; - buf[6] = (lower >> 16) & 0xff; - buf[7] = (lower >> 24) & 0xff; - ret = hackrf_ctrl_msg(dev, CMD_SAMPLE_RATE_SET, 0, 0, buf, 8); + uitmp = clamp(f->frequency, bands_adc_dac[0].rangelow, + bands_adc_dac[0].rangehigh); + if (vdev->vfl_dir == VFL_DIR_RX) { + dev->f_adc = uitmp; + set_bit(RX_ADC_FREQUENCY, &dev->flags); + } else { + dev->f_dac = uitmp; + set_bit(TX_DAC_FREQUENCY, &dev->flags); + } } else if (f->tuner == 1) { - dev->f_rf = clamp_t(unsigned int, f->frequency, - bands_rf[0].rangelow, bands_rf[0].rangehigh); - dev_dbg(dev->dev, "RF frequency=%u Hz\n", dev->f_rf); - upper = dev->f_rf / 1000000; - lower = dev->f_rf % 1000000; - buf[0] = (upper >> 0) & 0xff; - buf[1] = (upper >> 8) & 0xff; - buf[2] = (upper >> 16) & 0xff; - buf[3] = (upper >> 24) & 0xff; - buf[4] = (lower >> 0) & 0xff; - buf[5] = (lower >> 8) & 0xff; - buf[6] = (lower >> 16) & 0xff; - buf[7] = (lower >> 24) & 0xff; - ret = hackrf_ctrl_msg(dev, CMD_SET_FREQ, 0, 0, buf, 8); + uitmp = clamp(f->frequency, bands_rx_tx[0].rangelow, + bands_rx_tx[0].rangehigh); + if (vdev->vfl_dir == VFL_DIR_RX) { + dev->f_rx = uitmp; + set_bit(RX_RF_FREQUENCY, &dev->flags); + } else { + dev->f_tx = uitmp; + set_bit(TX_RF_FREQUENCY, &dev->flags); + } } else { ret = -EINVAL; + goto err; } + ret = hackrf_set_params(dev); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -780,22 +1155,32 @@ static int hackrf_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct hackrf_dev *dev = video_drvdata(file); + struct usb_interface *intf = dev->intf; + struct video_device *vdev = video_devdata(file); int ret; dev_dbg(dev->dev, "tuner=%d type=%d\n", f->tuner, f->type); if (f->tuner == 0) { - f->type = V4L2_TUNER_ADC; - f->frequency = dev->f_adc; - ret = 0; + f->type = V4L2_TUNER_SDR; + if (vdev->vfl_dir == VFL_DIR_RX) + f->frequency = dev->f_adc; + else + f->frequency = dev->f_dac; } else if (f->tuner == 1) { f->type = V4L2_TUNER_RF; - f->frequency = dev->f_rf; - ret = 0; + if (vdev->vfl_dir == VFL_DIR_RX) + f->frequency = dev->f_rx; + else + f->frequency = dev->f_tx; } else { ret = -EINVAL; + goto err; } + return 0; +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } @@ -809,17 +1194,17 @@ static int hackrf_enum_freq_bands(struct file *file, void *priv, band->tuner, band->type, band->index); if (band->tuner == 0) { - if (band->index >= ARRAY_SIZE(bands_adc)) { + if (band->index >= ARRAY_SIZE(bands_adc_dac)) { ret = -EINVAL; } else { - *band = bands_adc[band->index]; + *band = bands_adc_dac[band->index]; ret = 0; } } else if (band->tuner == 1) { - if (band->index >= ARRAY_SIZE(bands_rf)) { + if (band->index >= ARRAY_SIZE(bands_rx_tx)) { ret = -EINVAL; } else { - *band = bands_rf[band->index]; + *band = bands_rx_tx[band->index]; ret = 0; } } else { @@ -832,10 +1217,15 @@ static int hackrf_enum_freq_bands(struct file *file, void *priv, static const struct v4l2_ioctl_ops hackrf_ioctl_ops = { .vidioc_querycap = hackrf_querycap, - .vidioc_s_fmt_sdr_cap = hackrf_s_fmt_sdr_cap, - .vidioc_g_fmt_sdr_cap = hackrf_g_fmt_sdr_cap, - .vidioc_enum_fmt_sdr_cap = hackrf_enum_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = hackrf_try_fmt_sdr_cap, + .vidioc_s_fmt_sdr_cap = hackrf_s_fmt_sdr, + .vidioc_g_fmt_sdr_cap = hackrf_g_fmt_sdr, + .vidioc_enum_fmt_sdr_cap = hackrf_enum_fmt_sdr, + .vidioc_try_fmt_sdr_cap = hackrf_try_fmt_sdr, + + .vidioc_s_fmt_sdr_out = hackrf_s_fmt_sdr, + .vidioc_g_fmt_sdr_out = hackrf_g_fmt_sdr, + .vidioc_enum_fmt_sdr_out = hackrf_enum_fmt_sdr, + .vidioc_try_fmt_sdr_out = hackrf_try_fmt_sdr, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -843,6 +1233,7 @@ static const struct v4l2_ioctl_ops hackrf_ioctl_ops = { .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, @@ -850,6 +1241,9 @@ static const struct v4l2_ioctl_ops hackrf_ioctl_ops = { .vidioc_s_tuner = hackrf_s_tuner, .vidioc_g_tuner = hackrf_g_tuner, + .vidioc_s_modulator = hackrf_s_modulator, + .vidioc_g_modulator = hackrf_g_modulator, + .vidioc_s_frequency = hackrf_s_frequency, .vidioc_g_frequency = hackrf_g_frequency, .vidioc_enum_freq_bands = hackrf_enum_freq_bands, @@ -864,6 +1258,7 @@ static const struct v4l2_file_operations hackrf_fops = { .open = v4l2_fh_open, .release = vb2_fop_release, .read = vb2_fop_read, + .write = vb2_fop_write, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, @@ -880,135 +1275,93 @@ static void hackrf_video_release(struct v4l2_device *v) { struct hackrf_dev *dev = container_of(v, struct hackrf_dev, v4l2_dev); - v4l2_ctrl_handler_free(&dev->hdl); + dev_dbg(dev->dev, "\n"); + + v4l2_ctrl_handler_free(&dev->rx_ctrl_handler); + v4l2_ctrl_handler_free(&dev->tx_ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); kfree(dev); } -static int hackrf_set_bandwidth(struct hackrf_dev *dev) -{ - int ret, i; - u16 u16tmp, u16tmp2; - unsigned int bandwidth; - - static const struct { - u32 freq; - } bandwidth_lut[] = { - { 1750000}, /* 1.75 MHz */ - { 2500000}, /* 2.5 MHz */ - { 3500000}, /* 3.5 MHz */ - { 5000000}, /* 5 MHz */ - { 5500000}, /* 5.5 MHz */ - { 6000000}, /* 6 MHz */ - { 7000000}, /* 7 MHz */ - { 8000000}, /* 8 MHz */ - { 9000000}, /* 9 MHz */ - {10000000}, /* 10 MHz */ - {12000000}, /* 12 MHz */ - {14000000}, /* 14 MHz */ - {15000000}, /* 15 MHz */ - {20000000}, /* 20 MHz */ - {24000000}, /* 24 MHz */ - {28000000}, /* 28 MHz */ - }; - - dev_dbg(dev->dev, "bandwidth auto=%d->%d val=%d->%d f_adc=%u\n", - dev->bandwidth_auto->cur.val, - dev->bandwidth_auto->val, dev->bandwidth->cur.val, - dev->bandwidth->val, dev->f_adc); - - if (dev->bandwidth_auto->val == true) - bandwidth = dev->f_adc; - else - bandwidth = dev->bandwidth->val; - - for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { - if (bandwidth <= bandwidth_lut[i].freq) { - bandwidth = bandwidth_lut[i].freq; - break; - } - } - - dev->bandwidth->val = bandwidth; - dev->bandwidth->cur.val = bandwidth; - - dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth); - - u16tmp = 0; - u16tmp |= ((bandwidth >> 0) & 0xff) << 0; - u16tmp |= ((bandwidth >> 8) & 0xff) << 8; - u16tmp2 = 0; - u16tmp2 |= ((bandwidth >> 16) & 0xff) << 0; - u16tmp2 |= ((bandwidth >> 24) & 0xff) << 8; - - ret = hackrf_ctrl_msg(dev, CMD_BASEBAND_FILTER_BANDWIDTH_SET, - u16tmp, u16tmp2, NULL, 0); - if (ret) - dev_dbg(dev->dev, "failed=%d\n", ret); - - return ret; -} - -static int hackrf_set_lna_gain(struct hackrf_dev *dev) -{ - int ret; - u8 u8tmp; - - dev_dbg(dev->dev, "lna val=%d->%d\n", - dev->lna_gain->cur.val, dev->lna_gain->val); - - ret = hackrf_ctrl_msg(dev, CMD_SET_LNA_GAIN, 0, dev->lna_gain->val, - &u8tmp, 1); - if (ret) - dev_dbg(dev->dev, "failed=%d\n", ret); - - return ret; -} - -static int hackrf_set_if_gain(struct hackrf_dev *dev) +static int hackrf_s_ctrl_rx(struct v4l2_ctrl *ctrl) { + struct hackrf_dev *dev = container_of(ctrl->handler, + struct hackrf_dev, rx_ctrl_handler); + struct usb_interface *intf = dev->intf; int ret; - u8 u8tmp; - dev_dbg(dev->dev, "val=%d->%d\n", - dev->if_gain->cur.val, dev->if_gain->val); + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + set_bit(RX_BANDWIDTH, &dev->flags); + break; + case V4L2_CID_RF_TUNER_RF_GAIN: + set_bit(RX_RF_GAIN, &dev->flags); + break; + case V4L2_CID_RF_TUNER_LNA_GAIN: + set_bit(RX_LNA_GAIN, &dev->flags); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + set_bit(RX_IF_GAIN, &dev->flags); + break; + default: + dev_dbg(&intf->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); + ret = -EINVAL; + goto err; + } - ret = hackrf_ctrl_msg(dev, CMD_SET_VGA_GAIN, 0, dev->if_gain->val, - &u8tmp, 1); + ret = hackrf_set_params(dev); if (ret) - dev_dbg(dev->dev, "failed=%d\n", ret); + goto err; + return 0; +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } -static int hackrf_s_ctrl(struct v4l2_ctrl *ctrl) +static int hackrf_s_ctrl_tx(struct v4l2_ctrl *ctrl) { struct hackrf_dev *dev = container_of(ctrl->handler, - struct hackrf_dev, hdl); + struct hackrf_dev, tx_ctrl_handler); + struct usb_interface *intf = dev->intf; int ret; switch (ctrl->id) { case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH: - ret = hackrf_set_bandwidth(dev); + set_bit(TX_BANDWIDTH, &dev->flags); break; case V4L2_CID_RF_TUNER_LNA_GAIN: - ret = hackrf_set_lna_gain(dev); + set_bit(TX_LNA_GAIN, &dev->flags); break; - case V4L2_CID_RF_TUNER_IF_GAIN: - ret = hackrf_set_if_gain(dev); + case V4L2_CID_RF_TUNER_RF_GAIN: + set_bit(TX_RF_GAIN, &dev->flags); break; default: - dev_dbg(dev->dev, "unknown ctrl: id=%d name=%s\n", - ctrl->id, ctrl->name); + dev_dbg(&intf->dev, "unknown ctrl: id=%d name=%s\n", + ctrl->id, ctrl->name); ret = -EINVAL; + goto err; } + ret = hackrf_set_params(dev); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } -static const struct v4l2_ctrl_ops hackrf_ctrl_ops = { - .s_ctrl = hackrf_s_ctrl, +static const struct v4l2_ctrl_ops hackrf_ctrl_ops_rx = { + .s_ctrl = hackrf_s_ctrl_rx, +}; + +static const struct v4l2_ctrl_ops hackrf_ctrl_ops_tx = { + .s_ctrl = hackrf_s_ctrl_tx, }; static int hackrf_probe(struct usb_interface *intf, @@ -1019,19 +1372,29 @@ static int hackrf_probe(struct usb_interface *intf, u8 u8tmp, buf[BUF_SIZE]; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; + if (!dev) { + ret = -ENOMEM; + goto err; + } mutex_init(&dev->v4l2_lock); mutex_init(&dev->vb_queue_lock); - spin_lock_init(&dev->queued_bufs_lock); - INIT_LIST_HEAD(&dev->queued_bufs); + spin_lock_init(&dev->buffer_list_lock); + INIT_LIST_HEAD(&dev->rx_buffer_list); + INIT_LIST_HEAD(&dev->tx_buffer_list); + dev->intf = intf; dev->dev = &intf->dev; dev->udev = interface_to_usbdev(intf); - dev->f_adc = bands_adc[0].rangelow; - dev->f_rf = bands_rf[0].rangelow; dev->pixelformat = formats[0].pixelformat; dev->buffersize = formats[0].buffersize; + dev->f_adc = bands_adc_dac[0].rangelow; + dev->f_dac = bands_adc_dac[0].rangelow; + dev->f_rx = bands_rx_tx[0].rangelow; + dev->f_tx = bands_rx_tx[0].rangelow; + set_bit(RX_ADC_FREQUENCY, &dev->flags); + set_bit(TX_DAC_FREQUENCY, &dev->flags); + set_bit(RX_RF_FREQUENCY, &dev->flags); + set_bit(TX_RF_FREQUENCY, &dev->flags); /* Detect device */ ret = hackrf_ctrl_msg(dev, CMD_BOARD_ID_READ, 0, 0, &u8tmp, 1); @@ -1040,83 +1403,145 @@ static int hackrf_probe(struct usb_interface *intf, buf, BUF_SIZE); if (ret) { dev_err(dev->dev, "Could not detect board\n"); - goto err_free_mem; + goto err_kfree; } buf[BUF_SIZE - 1] = '\0'; - dev_info(dev->dev, "Board ID: %02x\n", u8tmp); dev_info(dev->dev, "Firmware version: %s\n", buf); - /* Init videobuf2 queue structure */ - dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - dev->vb_queue.drv_priv = dev; - dev->vb_queue.buf_struct_size = sizeof(struct hackrf_frame_buf); - dev->vb_queue.ops = &hackrf_vb2_ops; - dev->vb_queue.mem_ops = &vb2_vmalloc_memops; - dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&dev->vb_queue); + /* Init vb2 queue structure for receiver */ + dev->rx_vb2_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + dev->rx_vb2_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | + VB2_READ; + dev->rx_vb2_queue.ops = &hackrf_vb2_ops; + dev->rx_vb2_queue.mem_ops = &vb2_vmalloc_memops; + dev->rx_vb2_queue.drv_priv = dev; + dev->rx_vb2_queue.buf_struct_size = sizeof(struct hackrf_buffer); + dev->rx_vb2_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->rx_vb2_queue); if (ret) { - dev_err(dev->dev, "Could not initialize vb2 queue\n"); - goto err_free_mem; + dev_err(dev->dev, "Could not initialize rx vb2 queue\n"); + goto err_kfree; } - /* Init video_device structure */ - dev->vdev = hackrf_template; - dev->vdev.queue = &dev->vb_queue; - dev->vdev.queue->lock = &dev->vb_queue_lock; - video_set_drvdata(&dev->vdev, dev); + /* Init vb2 queue structure for transmitter */ + dev->tx_vb2_queue.type = V4L2_BUF_TYPE_SDR_OUTPUT; + dev->tx_vb2_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | + VB2_WRITE; + dev->tx_vb2_queue.ops = &hackrf_vb2_ops; + dev->tx_vb2_queue.mem_ops = &vb2_vmalloc_memops; + dev->tx_vb2_queue.drv_priv = dev; + dev->tx_vb2_queue.buf_struct_size = sizeof(struct hackrf_buffer); + dev->tx_vb2_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->tx_vb2_queue); + if (ret) { + dev_err(dev->dev, "Could not initialize tx vb2 queue\n"); + goto err_kfree; + } + + /* Register controls for receiver */ + v4l2_ctrl_handler_init(&dev->rx_ctrl_handler, 5); + dev->rx_bandwidth_auto = v4l2_ctrl_new_std(&dev->rx_ctrl_handler, + &hackrf_ctrl_ops_rx, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 0, 1); + dev->rx_bandwidth = v4l2_ctrl_new_std(&dev->rx_ctrl_handler, + &hackrf_ctrl_ops_rx, V4L2_CID_RF_TUNER_BANDWIDTH, + 1750000, 28000000, 50000, 1750000); + v4l2_ctrl_auto_cluster(2, &dev->rx_bandwidth_auto, 0, false); + dev->rx_rf_gain = v4l2_ctrl_new_std(&dev->rx_ctrl_handler, + &hackrf_ctrl_ops_rx, V4L2_CID_RF_TUNER_RF_GAIN, 0, 12, 12, 0); + dev->rx_lna_gain = v4l2_ctrl_new_std(&dev->rx_ctrl_handler, + &hackrf_ctrl_ops_rx, V4L2_CID_RF_TUNER_LNA_GAIN, 0, 40, 8, 0); + dev->rx_if_gain = v4l2_ctrl_new_std(&dev->rx_ctrl_handler, + &hackrf_ctrl_ops_rx, V4L2_CID_RF_TUNER_IF_GAIN, 0, 62, 2, 0); + if (dev->rx_ctrl_handler.error) { + ret = dev->rx_ctrl_handler.error; + dev_err(dev->dev, "Could not initialize controls\n"); + goto err_v4l2_ctrl_handler_free_rx; + } + v4l2_ctrl_grab(dev->rx_rf_gain, !hackrf_enable_rf_gain_ctrl); + v4l2_ctrl_handler_setup(&dev->rx_ctrl_handler); + + /* Register controls for transmitter */ + v4l2_ctrl_handler_init(&dev->tx_ctrl_handler, 4); + dev->tx_bandwidth_auto = v4l2_ctrl_new_std(&dev->tx_ctrl_handler, + &hackrf_ctrl_ops_tx, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 0, 1); + dev->tx_bandwidth = v4l2_ctrl_new_std(&dev->tx_ctrl_handler, + &hackrf_ctrl_ops_tx, V4L2_CID_RF_TUNER_BANDWIDTH, + 1750000, 28000000, 50000, 1750000); + v4l2_ctrl_auto_cluster(2, &dev->tx_bandwidth_auto, 0, false); + dev->tx_lna_gain = v4l2_ctrl_new_std(&dev->tx_ctrl_handler, + &hackrf_ctrl_ops_tx, V4L2_CID_RF_TUNER_LNA_GAIN, 0, 47, 1, 0); + dev->tx_rf_gain = v4l2_ctrl_new_std(&dev->tx_ctrl_handler, + &hackrf_ctrl_ops_tx, V4L2_CID_RF_TUNER_RF_GAIN, 0, 15, 15, 0); + if (dev->tx_ctrl_handler.error) { + ret = dev->tx_ctrl_handler.error; + dev_err(dev->dev, "Could not initialize controls\n"); + goto err_v4l2_ctrl_handler_free_tx; + } + v4l2_ctrl_grab(dev->tx_rf_gain, !hackrf_enable_rf_gain_ctrl); + v4l2_ctrl_handler_setup(&dev->tx_ctrl_handler); /* Register the v4l2_device structure */ dev->v4l2_dev.release = hackrf_video_release; ret = v4l2_device_register(&intf->dev, &dev->v4l2_dev); if (ret) { dev_err(dev->dev, "Failed to register v4l2-device (%d)\n", ret); - goto err_free_mem; + goto err_v4l2_ctrl_handler_free_tx; } - /* Register controls */ - v4l2_ctrl_handler_init(&dev->hdl, 4); - dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); - dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, - V4L2_CID_RF_TUNER_BANDWIDTH, - 1750000, 28000000, 50000, 1750000); - v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); - dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, - V4L2_CID_RF_TUNER_LNA_GAIN, 0, 40, 8, 0); - dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &hackrf_ctrl_ops, - V4L2_CID_RF_TUNER_IF_GAIN, 0, 62, 2, 0); - if (dev->hdl.error) { - ret = dev->hdl.error; - dev_err(dev->dev, "Could not initialize controls\n"); - goto err_free_controls; + /* Init video_device structure for receiver */ + dev->rx_vdev = hackrf_template; + dev->rx_vdev.queue = &dev->rx_vb2_queue; + dev->rx_vdev.queue->lock = &dev->vb_queue_lock; + dev->rx_vdev.v4l2_dev = &dev->v4l2_dev; + dev->rx_vdev.ctrl_handler = &dev->rx_ctrl_handler; + dev->rx_vdev.lock = &dev->v4l2_lock; + dev->rx_vdev.vfl_dir = VFL_DIR_RX; + video_set_drvdata(&dev->rx_vdev, dev); + ret = video_register_device(&dev->rx_vdev, VFL_TYPE_SDR, -1); + if (ret) { + dev_err(dev->dev, + "Failed to register as video device (%d)\n", ret); + goto err_v4l2_device_unregister; } - - v4l2_ctrl_handler_setup(&dev->hdl); - - dev->v4l2_dev.ctrl_handler = &dev->hdl; - dev->vdev.v4l2_dev = &dev->v4l2_dev; - dev->vdev.lock = &dev->v4l2_lock; - - ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); + dev_info(dev->dev, "Registered as %s\n", + video_device_node_name(&dev->rx_vdev)); + + /* Init video_device structure for transmitter */ + dev->tx_vdev = hackrf_template; + dev->tx_vdev.queue = &dev->tx_vb2_queue; + dev->tx_vdev.queue->lock = &dev->vb_queue_lock; + dev->tx_vdev.v4l2_dev = &dev->v4l2_dev; + dev->tx_vdev.ctrl_handler = &dev->tx_ctrl_handler; + dev->tx_vdev.lock = &dev->v4l2_lock; + dev->tx_vdev.vfl_dir = VFL_DIR_TX; + video_set_drvdata(&dev->tx_vdev, dev); + ret = video_register_device(&dev->tx_vdev, VFL_TYPE_SDR, -1); if (ret) { - dev_err(dev->dev, "Failed to register as video device (%d)\n", - ret); - goto err_unregister_v4l2_dev; + dev_err(dev->dev, + "Failed to register as video device (%d)\n", ret); + goto err_video_unregister_device_rx; } dev_info(dev->dev, "Registered as %s\n", - video_device_node_name(&dev->vdev)); + video_device_node_name(&dev->tx_vdev)); + dev_notice(dev->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); return 0; - -err_free_controls: - v4l2_ctrl_handler_free(&dev->hdl); -err_unregister_v4l2_dev: +err_video_unregister_device_rx: + video_unregister_device(&dev->rx_vdev); +err_v4l2_device_unregister: v4l2_device_unregister(&dev->v4l2_dev); -err_free_mem: +err_v4l2_ctrl_handler_free_tx: + v4l2_ctrl_handler_free(&dev->tx_ctrl_handler); +err_v4l2_ctrl_handler_free_rx: + v4l2_ctrl_handler_free(&dev->rx_ctrl_handler); +err_kfree: kfree(dev); +err: + dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } diff --git a/kernel/drivers/media/usb/msi2500/msi2500.c b/kernel/drivers/media/usb/msi2500/msi2500.c index efc761c78..e06a21a4f 100644 --- a/kernel/drivers/media/usb/msi2500/msi2500.c +++ b/kernel/drivers/media/usb/msi2500/msi2500.c @@ -1,4 +1,5 @@ /* + * Mirics MSi2500 driver * Mirics MSi3101 SDR Dongle driver * * Copyright (C) 2013 Antti Palosaari @@ -13,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * That driver is somehow based of pwc driver: * (C) 1999-2004 Nemosoft Unv. * (C) 2004-2006 Luc Saillard (luc@saillard.org) @@ -31,6 +28,7 @@ #include #include #include +#include #include #include @@ -115,11 +113,12 @@ static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); /* intermediate buffers with raw data from the USB device */ struct msi2500_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; struct list_head list; }; -struct msi2500_state { +struct msi2500_dev { struct device *dev; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -158,19 +157,19 @@ struct msi2500_state { /* Private functions */ static struct msi2500_frame_buf *msi2500_get_next_fill_buf( - struct msi2500_state *s) + struct msi2500_dev *dev) { unsigned long flags; struct msi2500_frame_buf *buf = NULL; - spin_lock_irqsave(&s->queued_bufs_lock, flags); - if (list_empty(&s->queued_bufs)) + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + if (list_empty(&dev->queued_bufs)) goto leave; - buf = list_entry(s->queued_bufs.next, struct msi2500_frame_buf, list); + buf = list_entry(dev->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); leave: - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); return buf; } @@ -256,8 +255,8 @@ leave: * signed 14-bit sample */ -static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, - unsigned int src_len) +static int msi2500_convert_stream(struct msi2500_dev *dev, u8 *dst, u8 *src, + unsigned int src_len) { unsigned int i, j, transactions, dst_len = 0; u32 sample[3]; @@ -268,26 +267,27 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, for (i = 0; i < transactions; i++) { sample[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0; - if (i == 0 && s->next_sample != sample[0]) { - dev_dbg_ratelimited(s->dev, - "%d samples lost, %d %08x:%08x\n", - sample[0] - s->next_sample, - src_len, s->next_sample, sample[0]); + if (i == 0 && dev->next_sample != sample[0]) { + dev_dbg_ratelimited(dev->dev, + "%d samples lost, %d %08x:%08x\n", + sample[0] - dev->next_sample, + src_len, dev->next_sample, + sample[0]); } /* * Dump all unknown 'garbage' data - maybe we will discover * someday if there is something rational... */ - dev_dbg_ratelimited(s->dev, "%*ph\n", 12, &src[4]); + dev_dbg_ratelimited(dev->dev, "%*ph\n", 12, &src[4]); src += 16; /* skip header */ - switch (s->pixelformat) { + switch (dev->pixelformat) { case V4L2_SDR_FMT_CU8: /* 504 x IQ samples */ { - s8 *s8src = (s8 *) src; - u8 *u8dst = (u8 *) dst; + s8 *s8src = (s8 *)src; + u8 *u8dst = (u8 *)dst; for (j = 0; j < 1008; j++) *u8dst++ = *s8src++ + 128; @@ -295,13 +295,13 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, src += 1008; dst += 1008; dst_len += 1008; - s->next_sample = sample[i] + 504; + dev->next_sample = sample[i] + 504; break; } case V4L2_SDR_FMT_CU16LE: /* 252 x IQ samples */ { - s16 *s16src = (s16 *) src; - u16 *u16dst = (u16 *) dst; + s16 *s16src = (s16 *)src; + u16 *u16dst = (u16 *)dst; struct {signed int x:14; } se; /* sign extension */ unsigned int utmp; @@ -317,38 +317,38 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, src += 1008; dst += 1008; dst_len += 1008; - s->next_sample = sample[i] + 252; + dev->next_sample = sample[i] + 252; break; } case MSI2500_PIX_FMT_SDR_MSI2500_384: /* 384 x IQ samples */ /* Dump unknown 'garbage' data */ - dev_dbg_ratelimited(s->dev, "%*ph\n", 24, &src[1000]); + dev_dbg_ratelimited(dev->dev, "%*ph\n", 24, &src[1000]); memcpy(dst, src, 984); src += 984 + 24; dst += 984; dst_len += 984; - s->next_sample = sample[i] + 384; + dev->next_sample = sample[i] + 384; break; case V4L2_SDR_FMT_CS8: /* 504 x IQ samples */ memcpy(dst, src, 1008); src += 1008; dst += 1008; dst_len += 1008; - s->next_sample = sample[i] + 504; + dev->next_sample = sample[i] + 504; break; case MSI2500_PIX_FMT_SDR_S12: /* 336 x IQ samples */ memcpy(dst, src, 1008); src += 1008; dst += 1008; dst_len += 1008; - s->next_sample = sample[i] + 336; + dev->next_sample = sample[i] + 336; break; case V4L2_SDR_FMT_CS14LE: /* 252 x IQ samples */ memcpy(dst, src, 1008); src += 1008; dst += 1008; dst_len += 1008; - s->next_sample = sample[i] + 252; + dev->next_sample = sample[i] + 252; break; default: break; @@ -356,17 +356,17 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, } /* calculate sample rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { + if (unlikely(time_is_before_jiffies(dev->jiffies_next))) { #define MSECS 10000UL unsigned int msecs = jiffies_to_msecs(jiffies - - s->jiffies_next + msecs_to_jiffies(MSECS)); - unsigned int samples = s->next_sample - s->sample; - - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample = s->next_sample; - dev_dbg(s->dev, "size=%u samples=%u msecs=%u sample rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); + dev->jiffies_next + msecs_to_jiffies(MSECS)); + unsigned int samples = dev->next_sample - dev->sample; + + dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + dev->sample = dev->next_sample; + dev_dbg(dev->dev, "size=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, + samples * 1000UL / msecs); } return dst_len; @@ -378,27 +378,28 @@ static int msi2500_convert_stream(struct msi2500_state *s, u8 *dst, u8 *src, */ static void msi2500_isoc_handler(struct urb *urb) { - struct msi2500_state *s = (struct msi2500_state *)urb->context; + struct msi2500_dev *dev = (struct msi2500_dev *)urb->context; int i, flen, fstatus; unsigned char *iso_buf = NULL; struct msi2500_frame_buf *fbuf; - if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) { - dev_dbg(s->dev, "URB (%p) unlinked %ssynchronuously\n", - urb, urb->status == -ENOENT ? "" : "a"); + if (unlikely(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dev_dbg(dev->dev, "URB (%p) unlinked %ssynchronuously\n", + urb, urb->status == -ENOENT ? "" : "a"); return; } if (unlikely(urb->status != 0)) { - dev_dbg(s->dev, "called with status %d\n", urb->status); + dev_dbg(dev->dev, "called with status %d\n", urb->status); /* Give up after a number of contiguous errors */ - if (++s->isoc_errors > MAX_ISOC_ERRORS) - dev_dbg(s->dev, "Too many ISOC errors, bailing out\n"); + if (++dev->isoc_errors > MAX_ISOC_ERRORS) + dev_dbg(dev->dev, "Too many ISOC errors, bailing out\n"); goto handler_end; } else { /* Reset ISOC error counter. We did get here, after all. */ - s->isoc_errors = 0; + dev->isoc_errors = 0; } /* Compact data */ @@ -408,9 +409,9 @@ static void msi2500_isoc_handler(struct urb *urb) /* Check frame error */ fstatus = urb->iso_frame_desc[i].status; if (unlikely(fstatus)) { - dev_dbg_ratelimited(s->dev, - "frame=%d/%d has error %d skipping\n", - i, urb->number_of_packets, fstatus); + dev_dbg_ratelimited(dev->dev, + "frame=%d/%d has error %d skipping\n", + i, urb->number_of_packets, fstatus); continue; } @@ -422,85 +423,85 @@ static void msi2500_isoc_handler(struct urb *urb) iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; /* Get free framebuffer */ - fbuf = msi2500_get_next_fill_buf(s); + fbuf = msi2500_get_next_fill_buf(dev); if (unlikely(fbuf == NULL)) { - s->vb_full++; - dev_dbg_ratelimited(s->dev, - "videobuf is full, %d packets dropped\n", - s->vb_full); + dev->vb_full++; + dev_dbg_ratelimited(dev->dev, + "videobuf is full, %d packets dropped\n", + dev->vb_full); continue; } /* fill framebuffer */ - ptr = vb2_plane_vaddr(&fbuf->vb, 0); - flen = msi2500_convert_stream(s, ptr, iso_buf, flen); - vb2_set_plane_payload(&fbuf->vb, 0, flen); - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + ptr = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0); + flen = msi2500_convert_stream(dev, ptr, iso_buf, flen); + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, flen); + vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); } handler_end: i = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(i != 0)) - dev_dbg(s->dev, "Error (%d) re-submitting urb\n", i); + dev_dbg(dev->dev, "Error (%d) re-submitting urb\n", i); } -static void msi2500_iso_stop(struct msi2500_state *s) +static void msi2500_iso_stop(struct msi2500_dev *dev) { int i; - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); /* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { - if (s->urbs[i]) { - dev_dbg(s->dev, "Unlinking URB %p\n", s->urbs[i]); - usb_kill_urb(s->urbs[i]); + if (dev->urbs[i]) { + dev_dbg(dev->dev, "Unlinking URB %p\n", dev->urbs[i]); + usb_kill_urb(dev->urbs[i]); } } } -static void msi2500_iso_free(struct msi2500_state *s) +static void msi2500_iso_free(struct msi2500_dev *dev) { int i; - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); /* Freeing ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { - if (s->urbs[i]) { - dev_dbg(s->dev, "Freeing URB\n"); - if (s->urbs[i]->transfer_buffer) { - usb_free_coherent(s->udev, - s->urbs[i]->transfer_buffer_length, - s->urbs[i]->transfer_buffer, - s->urbs[i]->transfer_dma); + if (dev->urbs[i]) { + dev_dbg(dev->dev, "Freeing URB\n"); + if (dev->urbs[i]->transfer_buffer) { + usb_free_coherent(dev->udev, + dev->urbs[i]->transfer_buffer_length, + dev->urbs[i]->transfer_buffer, + dev->urbs[i]->transfer_dma); } - usb_free_urb(s->urbs[i]); - s->urbs[i] = NULL; + usb_free_urb(dev->urbs[i]); + dev->urbs[i] = NULL; } } } /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static void msi2500_isoc_cleanup(struct msi2500_state *s) +static void msi2500_isoc_cleanup(struct msi2500_dev *dev) { - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - msi2500_iso_stop(s); - msi2500_iso_free(s); + msi2500_iso_stop(dev); + msi2500_iso_free(dev); } /* Both v4l2_lock and vb_queue_lock should be locked when calling this */ -static int msi2500_isoc_init(struct msi2500_state *s) +static int msi2500_isoc_init(struct msi2500_dev *dev) { struct urb *urb; int i, j, ret; - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - s->isoc_errors = 0; + dev->isoc_errors = 0; - ret = usb_set_interface(s->udev, 0, 1); + ret = usb_set_interface(dev->udev, 0, 1); if (ret) return ret; @@ -508,29 +509,29 @@ static int msi2500_isoc_init(struct msi2500_state *s) for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { - dev_err(s->dev, "Failed to allocate urb %d\n", i); - msi2500_isoc_cleanup(s); + dev_err(dev->dev, "Failed to allocate urb %d\n", i); + msi2500_isoc_cleanup(dev); return -ENOMEM; } - s->urbs[i] = urb; - dev_dbg(s->dev, "Allocated URB at 0x%p\n", urb); + dev->urbs[i] = urb; + dev_dbg(dev->dev, "Allocated URB at 0x%p\n", urb); urb->interval = 1; - urb->dev = s->udev; - urb->pipe = usb_rcvisocpipe(s->udev, 0x81); + urb->dev = dev->udev; + urb->pipe = usb_rcvisocpipe(dev->udev, 0x81); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(s->udev, + urb->transfer_buffer = usb_alloc_coherent(dev->udev, ISO_BUFFER_SIZE, GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - dev_err(s->dev, "Failed to allocate urb buffer %d\n", - i); - msi2500_isoc_cleanup(s); + dev_err(dev->dev, + "Failed to allocate urb buffer %d\n", i); + msi2500_isoc_cleanup(dev); return -ENOMEM; } urb->transfer_buffer_length = ISO_BUFFER_SIZE; urb->complete = msi2500_isoc_handler; - urb->context = s; + urb->context = dev; urb->start_frame = 0; urb->number_of_packets = ISO_FRAMES_PER_DESC; for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { @@ -541,14 +542,15 @@ static int msi2500_isoc_init(struct msi2500_state *s) /* link */ for (i = 0; i < MAX_ISO_BUFS; i++) { - ret = usb_submit_urb(s->urbs[i], GFP_KERNEL); + ret = usb_submit_urb(dev->urbs[i], GFP_KERNEL); if (ret) { - dev_err(s->dev, "usb_submit_urb %d failed with error %d\n", - i, ret); - msi2500_isoc_cleanup(s); + dev_err(dev->dev, + "usb_submit_urb %d failed with error %d\n", + i, ret); + msi2500_isoc_cleanup(dev); return ret; } - dev_dbg(s->dev, "URB 0x%p submitted.\n", s->urbs[i]); + dev_dbg(dev->dev, "URB 0x%p submitted.\n", dev->urbs[i]); } /* All is done... */ @@ -556,56 +558,56 @@ static int msi2500_isoc_init(struct msi2500_state *s) } /* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs(struct msi2500_state *s) +static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) { unsigned long flags; - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - spin_lock_irqsave(&s->queued_bufs_lock, flags); - while (!list_empty(&s->queued_bufs)) { + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + while (!list_empty(&dev->queued_bufs)) { struct msi2500_frame_buf *buf; - buf = list_entry(s->queued_bufs.next, struct msi2500_frame_buf, - list); + buf = list_entry(dev->queued_bufs.next, + struct msi2500_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } /* The user yanked out the cable... */ static void msi2500_disconnect(struct usb_interface *intf) { struct v4l2_device *v = usb_get_intfdata(intf); - struct msi2500_state *s = - container_of(v, struct msi2500_state, v4l2_dev); + struct msi2500_dev *dev = + container_of(v, struct msi2500_dev, v4l2_dev); - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - mutex_lock(&s->vb_queue_lock); - mutex_lock(&s->v4l2_lock); + mutex_lock(&dev->vb_queue_lock); + mutex_lock(&dev->v4l2_lock); /* No need to keep the urbs around after disconnection */ - s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); - spi_unregister_master(s->master); - mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); - - v4l2_device_put(&s->v4l2_dev); + dev->udev = NULL; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(&dev->vdev); + spi_unregister_master(dev->master); + mutex_unlock(&dev->v4l2_lock); + mutex_unlock(&dev->vb_queue_lock); + + v4l2_device_put(&dev->v4l2_dev); } static int msi2500_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) + struct v4l2_capability *cap) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); - usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + strlcpy(cap->card, dev->vdev.name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_TUNER; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -614,43 +616,47 @@ static int msi2500_querycap(struct file *file, void *fh, /* Videobuf2 operations */ static int msi2500_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *fmt, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) + const void *parg, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + void *alloc_ctxs[]) { - struct msi2500_state *s = vb2_get_drv_priv(vq); + struct msi2500_dev *dev = vb2_get_drv_priv(vq); - dev_dbg(s->dev, "nbuffers=%d\n", *nbuffers); + dev_dbg(dev->dev, "nbuffers=%d\n", *nbuffers); /* Absolute min and max number of buffers available for mmap() */ *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32); *nplanes = 1; - sizes[0] = PAGE_ALIGN(s->buffersize); - dev_dbg(s->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); + sizes[0] = PAGE_ALIGN(dev->buffersize); + dev_dbg(dev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); return 0; } static void msi2500_buf_queue(struct vb2_buffer *vb) { - struct msi2500_state *s = vb2_get_drv_priv(vb->vb2_queue); - struct msi2500_frame_buf *buf = - container_of(vb, struct msi2500_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct msi2500_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct msi2500_frame_buf *buf = container_of(vbuf, + struct msi2500_frame_buf, + vb); unsigned long flags; /* Check the device has not disconnected between prep and queuing */ - if (unlikely(!s->udev)) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + if (unlikely(!dev->udev)) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); return; } - spin_lock_irqsave(&s->queued_bufs_lock, flags); - list_add_tail(&buf->list, &s->queued_bufs); - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &dev->queued_bufs); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } #define CMD_WREG 0x41 #define CMD_START_STREAMING 0x43 #define CMD_STOP_STREAMING 0x45 -#define CMD_READ_UNKNOW 0x48 +#define CMD_READ_UNKNOWN 0x48 #define msi2500_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \ char *_direction; \ @@ -663,7 +669,7 @@ static void msi2500_buf_queue(struct vb2_buffer *vb) _l & 0xff, _l >> 8, _direction, _l, _b); \ } -static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data) +static int msi2500_ctrl_msg(struct msi2500_dev *dev, u8 cmd, u32 data) { int ret; u8 request = cmd; @@ -671,39 +677,38 @@ static int msi2500_ctrl_msg(struct msi2500_state *s, u8 cmd, u32 data) u16 value = (data >> 0) & 0xffff; u16 index = (data >> 16) & 0xffff; - msi2500_dbg_usb_control_msg(s->dev, - request, requesttype, value, index, NULL, 0); - ret = usb_control_msg(s->udev, usb_sndctrlpipe(s->udev, 0), - request, requesttype, value, index, NULL, 0, 2000); + msi2500_dbg_usb_control_msg(dev->dev, request, requesttype, + value, index, NULL, 0); + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), request, + requesttype, value, index, NULL, 0, 2000); if (ret) - dev_err(s->dev, "failed %d, cmd %02x, data %04x\n", - ret, cmd, data); + dev_err(dev->dev, "failed %d, cmd %02x, data %04x\n", + ret, cmd, data); return ret; } -#define F_REF 24000000 -#define DIV_R_IN 2 -static int msi2500_set_usb_adc(struct msi2500_state *s) +static int msi2500_set_usb_adc(struct msi2500_dev *dev) { - int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract; + int ret; + unsigned int f_vco, f_sr, div_n, k, k_cw, div_out; u32 reg3, reg4, reg7; struct v4l2_ctrl *bandwidth_auto; struct v4l2_ctrl *bandwidth; - f_sr = s->f_adc; + f_sr = dev->f_adc; /* set tuner, subdev, filters according to sampling rate */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, + bandwidth_auto = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { - bandwidth = v4l2_ctrl_find(&s->hdl, + bandwidth = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); - v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); + v4l2_ctrl_s_ctrl(bandwidth, dev->f_adc); } /* select stream format */ - switch (s->pixelformat) { + switch (dev->pixelformat) { case V4L2_SDR_FMT_CU8: reg7 = 0x000c9407; /* 504 */ break; @@ -727,6 +732,21 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) break; } + /* + * Fractional-N synthesizer + * + * +----------------------------------------+ + * v | + * Fref +----+ +-------+ +-----+ +------+ +---+ + * ------> | PD | --> | VCO | --> | /2 | ------> | /N.F | <-- | K | + * +----+ +-------+ +-----+ +------+ +---+ + * | + * | + * v + * +-------+ +-----+ Fout + * | /Rout | --> | /12 | ------> + * +-------+ +-----+ + */ /* * Synthesizer config is just a educated guess... * @@ -754,10 +774,14 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) * * VCO 202000000 - 720000000++ */ + + #define F_REF 24000000 + #define DIV_PRE_N 2 + #define DIV_LO_OUT 12 reg3 = 0x01000303; reg4 = 0x00000004; - /* XXX: Filters? AGC? */ + /* XXX: Filters? AGC? VCO band? */ if (f_sr < 6000000) reg3 |= 0x1 << 20; else if (f_sr < 7000000) @@ -767,54 +791,55 @@ static int msi2500_set_usb_adc(struct msi2500_state *s) else reg3 |= 0xd << 20; - for (div_r_out = 4; div_r_out < 16; div_r_out += 2) { - f_vco = f_sr * div_r_out * 12; - dev_dbg(s->dev, "div_r_out=%d f_vco=%d\n", div_r_out, f_vco); + for (div_out = 4; div_out < 16; div_out += 2) { + f_vco = f_sr * div_out * DIV_LO_OUT; + dev_dbg(dev->dev, "div_out=%u f_vco=%u\n", div_out, f_vco); if (f_vco >= 202000000) break; } - div_n = f_vco / (F_REF * DIV_R_IN); - div_m = f_vco % (F_REF * DIV_R_IN); - fract = 0x200000ul * div_m / (F_REF * DIV_R_IN); + /* Calculate PLL integer and fractional control word. */ + div_n = div_u64_rem(f_vco, DIV_PRE_N * F_REF, &k); + k_cw = div_u64((u64) k * 0x200000, DIV_PRE_N * F_REF); reg3 |= div_n << 16; - reg3 |= (div_r_out / 2 - 1) << 10; - reg3 |= ((fract >> 20) & 0x000001) << 15; /* [20] */ - reg4 |= ((fract >> 0) & 0x0fffff) << 8; /* [19:0] */ + reg3 |= (div_out / 2 - 1) << 10; + reg3 |= ((k_cw >> 20) & 0x000001) << 15; /* [20] */ + reg4 |= ((k_cw >> 0) & 0x0fffff) << 8; /* [19:0] */ - dev_dbg(s->dev, "f_sr=%d f_vco=%d div_n=%d div_m=%d div_r_out=%d reg3=%08x reg4=%08x\n", - f_sr, f_vco, div_n, div_m, div_r_out, reg3, reg4); + dev_dbg(dev->dev, + "f_sr=%u f_vco=%u div_n=%u k=%u div_out=%u reg3=%08x reg4=%08x\n", + f_sr, f_vco, div_n, k, div_out, reg3, reg4); - ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00608008); + ret = msi2500_ctrl_msg(dev, CMD_WREG, 0x00608008); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00000c05); + ret = msi2500_ctrl_msg(dev, CMD_WREG, 0x00000c05); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00020000); + ret = msi2500_ctrl_msg(dev, CMD_WREG, 0x00020000); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00480102); + ret = msi2500_ctrl_msg(dev, CMD_WREG, 0x00480102); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, 0x00f38008); + ret = msi2500_ctrl_msg(dev, CMD_WREG, 0x00f38008); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, reg7); + ret = msi2500_ctrl_msg(dev, CMD_WREG, reg7); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, reg4); + ret = msi2500_ctrl_msg(dev, CMD_WREG, reg4); if (ret) goto err; - ret = msi2500_ctrl_msg(s, CMD_WREG, reg3); + ret = msi2500_ctrl_msg(dev, CMD_WREG, reg3); if (ret) goto err; err: @@ -823,57 +848,57 @@ err: static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct msi2500_state *s = vb2_get_drv_priv(vq); + struct msi2500_dev *dev = vb2_get_drv_priv(vq); int ret; - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - if (!s->udev) + if (!dev->udev) return -ENODEV; - if (mutex_lock_interruptible(&s->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) return -ERESTARTSYS; /* wake-up tuner */ - v4l2_subdev_call(s->v4l2_subdev, core, s_power, 1); + v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); - ret = msi2500_set_usb_adc(s); + ret = msi2500_set_usb_adc(dev); - ret = msi2500_isoc_init(s); + ret = msi2500_isoc_init(dev); if (ret) - msi2500_cleanup_queued_bufs(s); + msi2500_cleanup_queued_bufs(dev); - ret = msi2500_ctrl_msg(s, CMD_START_STREAMING, 0); + ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0); - mutex_unlock(&s->v4l2_lock); + mutex_unlock(&dev->v4l2_lock); return ret; } static void msi2500_stop_streaming(struct vb2_queue *vq) { - struct msi2500_state *s = vb2_get_drv_priv(vq); + struct msi2500_dev *dev = vb2_get_drv_priv(vq); - dev_dbg(s->dev, "\n"); + dev_dbg(dev->dev, "\n"); - mutex_lock(&s->v4l2_lock); + mutex_lock(&dev->v4l2_lock); - if (s->udev) - msi2500_isoc_cleanup(s); + if (dev->udev) + msi2500_isoc_cleanup(dev); - msi2500_cleanup_queued_bufs(s); + msi2500_cleanup_queued_bufs(dev); /* according to tests, at least 700us delay is required */ msleep(20); - if (!msi2500_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { + if (!msi2500_ctrl_msg(dev, CMD_STOP_STREAMING, 0)) { /* sleep USB IF / ADC */ - msi2500_ctrl_msg(s, CMD_WREG, 0x01000003); + msi2500_ctrl_msg(dev, CMD_WREG, 0x01000003); } /* sleep tuner */ - v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); + v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 0); - mutex_unlock(&s->v4l2_lock); + mutex_unlock(&dev->v4l2_lock); } static struct vb2_ops msi2500_vb2_ops = { @@ -886,13 +911,13 @@ static struct vb2_ops msi2500_vb2_ops = { }; static int msi2500_enum_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) + struct v4l2_fmtdesc *f) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); - dev_dbg(s->dev, "index=%d\n", f->index); + dev_dbg(dev->dev, "index=%d\n", f->index); - if (f->index >= s->num_formats) + if (f->index >= dev->num_formats) return -EINVAL; strlcpy(f->description, formats[f->index].name, sizeof(f->description)); @@ -902,45 +927,45 @@ static int msi2500_enum_fmt_sdr_cap(struct file *file, void *priv, } static int msi2500_g_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); - dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", - (char *)&s->pixelformat); + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&dev->pixelformat); - f->fmt.sdr.pixelformat = s->pixelformat; - f->fmt.sdr.buffersize = s->buffersize; + f->fmt.sdr.pixelformat = dev->pixelformat; + f->fmt.sdr.buffersize = dev->buffersize; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); return 0; } static int msi2500_s_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { - struct msi2500_state *s = video_drvdata(file); - struct vb2_queue *q = &s->vb_queue; + struct msi2500_dev *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_queue; int i; - dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", - (char *)&f->fmt.sdr.pixelformat); + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); if (vb2_is_busy(q)) return -EBUSY; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < s->num_formats; i++) { + for (i = 0; i < dev->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = formats[i].pixelformat; - s->buffersize = formats[i].buffersize; + dev->pixelformat = formats[i].pixelformat; + dev->buffersize = formats[i].buffersize; f->fmt.sdr.buffersize = formats[i].buffersize; return 0; } } - s->pixelformat = formats[0].pixelformat; - s->buffersize = formats[0].buffersize; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; f->fmt.sdr.pixelformat = formats[0].pixelformat; f->fmt.sdr.buffersize = formats[0].buffersize; @@ -948,16 +973,16 @@ static int msi2500_s_fmt_sdr_cap(struct file *file, void *priv, } static int msi2500_try_fmt_sdr_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int i; - dev_dbg(s->dev, "pixelformat fourcc %4.4s\n", - (char *)&f->fmt.sdr.pixelformat); + dev_dbg(dev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < s->num_formats; i++) { + for (i = 0; i < dev->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { f->fmt.sdr.buffersize = formats[i].buffersize; return 0; @@ -971,17 +996,17 @@ static int msi2500_try_fmt_sdr_cap(struct file *file, void *priv, } static int msi2500_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *v) + const struct v4l2_tuner *v) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int ret; - dev_dbg(s->dev, "index=%d\n", v->index); + dev_dbg(dev->dev, "index=%d\n", v->index); if (v->index == 0) ret = 0; else if (v->index == 1) - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_tuner, v); + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_tuner, v); else ret = -EINVAL; @@ -990,10 +1015,10 @@ static int msi2500_s_tuner(struct file *file, void *priv, static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int ret; - dev_dbg(s->dev, "index=%d\n", v->index); + dev_dbg(dev->dev, "index=%d\n", v->index); if (v->index == 0) { strlcpy(v->name, "Mirics MSi2500", sizeof(v->name)); @@ -1003,7 +1028,7 @@ static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) v->rangehigh = 15000000; ret = 0; } else if (v->index == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_tuner, v); + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_tuner, v); } else { ret = -EINVAL; } @@ -1012,19 +1037,19 @@ static int msi2500_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) } static int msi2500_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + struct v4l2_frequency *f) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int ret = 0; - dev_dbg(s->dev, "tuner=%d type=%d\n", f->tuner, f->type); + dev_dbg(dev->dev, "tuner=%d type=%d\n", f->tuner, f->type); if (f->tuner == 0) { - f->frequency = s->f_adc; + f->frequency = dev->f_adc; ret = 0; } else if (f->tuner == 1) { f->type = V4L2_TUNER_RF; - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_frequency, f); + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_frequency, f); } else { ret = -EINVAL; } @@ -1033,22 +1058,22 @@ static int msi2500_g_frequency(struct file *file, void *priv, } static int msi2500_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) + const struct v4l2_frequency *f) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int ret; - dev_dbg(s->dev, "tuner=%d type=%d frequency=%u\n", - f->tuner, f->type, f->frequency); + dev_dbg(dev->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); if (f->tuner == 0) { - s->f_adc = clamp_t(unsigned int, f->frequency, - bands[0].rangelow, - bands[0].rangehigh); - dev_dbg(s->dev, "ADC frequency=%u Hz\n", s->f_adc); - ret = msi2500_set_usb_adc(s); + dev->f_adc = clamp_t(unsigned int, f->frequency, + bands[0].rangelow, + bands[0].rangehigh); + dev_dbg(dev->dev, "ADC frequency=%u Hz\n", dev->f_adc); + ret = msi2500_set_usb_adc(dev); } else if (f->tuner == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f); + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_frequency, f); } else { ret = -EINVAL; } @@ -1057,13 +1082,13 @@ static int msi2500_s_frequency(struct file *file, void *priv, } static int msi2500_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) + struct v4l2_frequency_band *band) { - struct msi2500_state *s = video_drvdata(file); + struct msi2500_dev *dev = video_drvdata(file); int ret; - dev_dbg(s->dev, "tuner=%d type=%d index=%d\n", - band->tuner, band->type, band->index); + dev_dbg(dev->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->tuner == 0) { if (band->index >= ARRAY_SIZE(bands)) { @@ -1073,8 +1098,8 @@ static int msi2500_enum_freq_bands(struct file *file, void *priv, ret = 0; } } else if (band->tuner == 1) { - ret = v4l2_subdev_call(s->v4l2_subdev, tuner, - enum_freq_bands, band); + ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, + enum_freq_bands, band); } else { ret = -EINVAL; } @@ -1131,29 +1156,28 @@ static struct video_device msi2500_template = { static void msi2500_video_release(struct v4l2_device *v) { - struct msi2500_state *s = - container_of(v, struct msi2500_state, v4l2_dev); + struct msi2500_dev *dev = container_of(v, struct msi2500_dev, v4l2_dev); - v4l2_ctrl_handler_free(&s->hdl); - v4l2_device_unregister(&s->v4l2_dev); - kfree(s); + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); } static int msi2500_transfer_one_message(struct spi_master *master, - struct spi_message *m) + struct spi_message *m) { - struct msi2500_state *s = spi_master_get_devdata(master); + struct msi2500_dev *dev = spi_master_get_devdata(master); struct spi_transfer *t; int ret = 0; u32 data; list_for_each_entry(t, &m->transfers, transfer_list) { - dev_dbg(s->dev, "msg=%*ph\n", t->len, t->tx_buf); + dev_dbg(dev->dev, "msg=%*ph\n", t->len, t->tx_buf); data = 0x09; /* reg 9 is SPI adapter */ data |= ((u8 *)t->tx_buf)[0] << 8; data |= ((u8 *)t->tx_buf)[1] << 16; data |= ((u8 *)t->tx_buf)[2] << 24; - ret = msi2500_ctrl_msg(s, CMD_WREG, data); + ret = msi2500_ctrl_msg(dev, CMD_WREG, data); } m->status = ret; @@ -1162,9 +1186,9 @@ static int msi2500_transfer_one_message(struct spi_master *master, } static int msi2500_probe(struct usb_interface *intf, - const struct usb_device_id *id) + const struct usb_device_id *id) { - struct msi2500_state *s; + struct msi2500_dev *dev; struct v4l2_subdev *sd; struct spi_master *master; int ret; @@ -1175,65 +1199,65 @@ static int msi2500_probe(struct usb_interface *intf, .max_speed_hz = 12000000, }; - s = kzalloc(sizeof(struct msi2500_state), GFP_KERNEL); - if (s == NULL) { - dev_err(&intf->dev, "Could not allocate memory for state\n"); - return -ENOMEM; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; } - mutex_init(&s->v4l2_lock); - mutex_init(&s->vb_queue_lock); - spin_lock_init(&s->queued_bufs_lock); - INIT_LIST_HEAD(&s->queued_bufs); - s->dev = &intf->dev; - s->udev = interface_to_usbdev(intf); - s->f_adc = bands[0].rangelow; - s->pixelformat = formats[0].pixelformat; - s->buffersize = formats[0].buffersize; - s->num_formats = NUM_FORMATS; + mutex_init(&dev->v4l2_lock); + mutex_init(&dev->vb_queue_lock); + spin_lock_init(&dev->queued_bufs_lock); + INIT_LIST_HEAD(&dev->queued_bufs); + dev->dev = &intf->dev; + dev->udev = interface_to_usbdev(intf); + dev->f_adc = bands[0].rangelow; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + dev->num_formats = NUM_FORMATS; if (!msi2500_emulated_fmt) - s->num_formats -= 2; + dev->num_formats -= 2; /* Init videobuf2 queue structure */ - s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct msi2500_frame_buf); - s->vb_queue.ops = &msi2500_vb2_ops; - s->vb_queue.mem_ops = &vb2_vmalloc_memops; - s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&s->vb_queue); + dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + dev->vb_queue.drv_priv = dev; + dev->vb_queue.buf_struct_size = sizeof(struct msi2500_frame_buf); + dev->vb_queue.ops = &msi2500_vb2_ops; + dev->vb_queue.mem_ops = &vb2_vmalloc_memops; + dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->vb_queue); if (ret) { - dev_err(s->dev, "Could not initialize vb2 queue\n"); + dev_err(dev->dev, "Could not initialize vb2 queue\n"); goto err_free_mem; } /* Init video_device structure */ - s->vdev = msi2500_template; - s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; - video_set_drvdata(&s->vdev, s); + dev->vdev = msi2500_template; + dev->vdev.queue = &dev->vb_queue; + dev->vdev.queue->lock = &dev->vb_queue_lock; + video_set_drvdata(&dev->vdev, dev); /* Register the v4l2_device structure */ - s->v4l2_dev.release = msi2500_video_release; - ret = v4l2_device_register(&intf->dev, &s->v4l2_dev); + dev->v4l2_dev.release = msi2500_video_release; + ret = v4l2_device_register(&intf->dev, &dev->v4l2_dev); if (ret) { - dev_err(s->dev, "Failed to register v4l2-device (%d)\n", ret); + dev_err(dev->dev, "Failed to register v4l2-device (%d)\n", ret); goto err_free_mem; } /* SPI master adapter */ - master = spi_alloc_master(s->dev, 0); + master = spi_alloc_master(dev->dev, 0); if (master == NULL) { ret = -ENOMEM; goto err_unregister_v4l2_dev; } - s->master = master; + dev->master = master; master->bus_num = 0; master->num_chipselect = 1; master->transfer_one_message = msi2500_transfer_one_message; - spi_master_set_devdata(master, s); + spi_master_set_devdata(master, dev); ret = spi_register_master(master); if (ret) { spi_master_put(master); @@ -1241,57 +1265,57 @@ static int msi2500_probe(struct usb_interface *intf, } /* load v4l2 subdevice */ - sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info); - s->v4l2_subdev = sd; + sd = v4l2_spi_new_subdev(&dev->v4l2_dev, master, &board_info); + dev->v4l2_subdev = sd; if (sd == NULL) { - dev_err(s->dev, "cannot get v4l2 subdevice\n"); + dev_err(dev->dev, "cannot get v4l2 subdevice\n"); ret = -ENODEV; goto err_unregister_master; } /* Register controls */ - v4l2_ctrl_handler_init(&s->hdl, 0); - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(s->dev, "Could not initialize controls\n"); + v4l2_ctrl_handler_init(&dev->hdl, 0); + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(dev->dev, "Could not initialize controls\n"); goto err_free_controls; } /* currently all controls are from subdev */ - v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); + v4l2_ctrl_add_handler(&dev->hdl, sd->ctrl_handler, NULL); - s->v4l2_dev.ctrl_handler = &s->hdl; - s->vdev.v4l2_dev = &s->v4l2_dev; - s->vdev.lock = &s->v4l2_lock; + dev->v4l2_dev.ctrl_handler = &dev->hdl; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->v4l2_lock; - ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); if (ret) { - dev_err(s->dev, "Failed to register as video device (%d)\n", - ret); + dev_err(dev->dev, + "Failed to register as video device (%d)\n", ret); goto err_unregister_v4l2_dev; } - dev_info(s->dev, "Registered as %s\n", - video_device_node_name(&s->vdev)); - dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); - + dev_info(dev->dev, "Registered as %s\n", + video_device_node_name(&dev->vdev)); + dev_notice(dev->dev, + "SDR API is still slightly experimental and functionality changes may follow\n"); return 0; - err_free_controls: - v4l2_ctrl_handler_free(&s->hdl); + v4l2_ctrl_handler_free(&dev->hdl); err_unregister_master: - spi_unregister_master(s->master); + spi_unregister_master(dev->master); err_unregister_v4l2_dev: - v4l2_device_unregister(&s->v4l2_dev); + v4l2_device_unregister(&dev->v4l2_dev); err_free_mem: - kfree(s); + kfree(dev); +err: return ret; } /* USB device ID list */ static struct usb_device_id msi2500_id_table[] = { - { USB_DEVICE(0x1df7, 0x2500) }, /* Mirics MSi3101 SDR Dongle */ - { USB_DEVICE(0x2040, 0xd300) }, /* Hauppauge WinTV 133559 LF */ - { } + {USB_DEVICE(0x1df7, 0x2500)}, /* Mirics MSi3101 SDR Dongle */ + {USB_DEVICE(0x2040, 0xd300)}, /* Hauppauge WinTV 133559 LF */ + {} }; MODULE_DEVICE_TABLE(usb, msi2500_id_table); diff --git a/kernel/drivers/media/usb/pvrusb2/pvrusb2-context.c b/kernel/drivers/media/usb/pvrusb2/pvrusb2-context.c index 924fc4c60..fd888a604 100644 --- a/kernel/drivers/media/usb/pvrusb2/pvrusb2-context.c +++ b/kernel/drivers/media/usb/pvrusb2/pvrusb2-context.c @@ -398,7 +398,8 @@ int pvr2_channel_claim_stream(struct pvr2_channel *cp, if (!sp) break; sp->user = cp; cp->stream = sp; - } while (0); pvr2_context_exit(cp->mc_head); + } while (0); + pvr2_context_exit(cp->mc_head); return code; } diff --git a/kernel/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/kernel/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 930593d70..0533ef20d 100644 --- a/kernel/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/kernel/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2602,14 +2602,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, "Error registering with v4l core, giving up"); goto fail; } - mutex_lock(&pvr2_unit_mtx); do { + mutex_lock(&pvr2_unit_mtx); + do { for (idx = 0; idx < PVR_NUM; idx++) { if (unit_pointers[idx]) continue; hdw->unit_number = idx; unit_pointers[idx] = hdw; break; } - } while (0); mutex_unlock(&pvr2_unit_mtx); + } while (0); + mutex_unlock(&pvr2_unit_mtx); cnt1 = 0; cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2"); @@ -2730,13 +2732,15 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_i2c_core_done(hdw); v4l2_device_unregister(&hdw->v4l2_dev); pvr2_hdw_remove_usb_stuff(hdw); - mutex_lock(&pvr2_unit_mtx); do { + mutex_lock(&pvr2_unit_mtx); + do { if ((hdw->unit_number >= 0) && (hdw->unit_number < PVR_NUM) && (unit_pointers[hdw->unit_number] == hdw)) { unit_pointers[hdw->unit_number] = NULL; } - } while (0); mutex_unlock(&pvr2_unit_mtx); + } while (0); + mutex_unlock(&pvr2_unit_mtx); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); kfree(hdw); @@ -2958,14 +2962,17 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) } if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { - struct v4l2_mbus_framefmt fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.width = hdw->res_hor_val; - fmt.height = hdw->res_ver_val; - fmt.code = MEDIA_BUS_FMT_FIXED; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + format.format.width = hdw->res_hor_val; + format.format.height = hdw->res_ver_val; + format.format.code = MEDIA_BUS_FMT_FIXED; pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)", - fmt.width, fmt.height); - v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_mbus_fmt, &fmt); + format.format.width, format.format.height); + v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt, + NULL, &format); } if (hdw->srate_dirty || hdw->force_dirty) { @@ -3343,14 +3350,16 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp) void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) { int nr = pvr2_hdw_get_unit_number(hdw); - LOCK_TAKE(hdw->big_lock); do { + LOCK_TAKE(hdw->big_lock); + do { printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status); pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); pvr2_hdw_state_log_state(hdw); printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); - } while (0); LOCK_GIVE(hdw->big_lock); + } while (0); + LOCK_GIVE(hdw->big_lock); } diff --git a/kernel/drivers/media/usb/pvrusb2/pvrusb2-io.c b/kernel/drivers/media/usb/pvrusb2/pvrusb2-io.c index 0c08f22bd..d860344de 100644 --- a/kernel/drivers/media/usb/pvrusb2/pvrusb2-io.c +++ b/kernel/drivers/media/usb/pvrusb2/pvrusb2-io.c @@ -514,12 +514,14 @@ void pvr2_stream_set_callback(struct pvr2_stream *sp, void *data) { unsigned long irq_flags; - mutex_lock(&sp->mutex); do { + mutex_lock(&sp->mutex); + do { spin_lock_irqsave(&sp->list_lock,irq_flags); sp->callback_data = data; sp->callback_func = func; spin_unlock_irqrestore(&sp->list_lock,irq_flags); - } while(0); mutex_unlock(&sp->mutex); + } while(0); + mutex_unlock(&sp->mutex); } void pvr2_stream_get_stats(struct pvr2_stream *sp, @@ -554,10 +556,12 @@ int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt) { int ret; if (sp->buffer_target_count == cnt) return 0; - mutex_lock(&sp->mutex); do { + mutex_lock(&sp->mutex); + do { sp->buffer_target_count = cnt; ret = pvr2_stream_achieve_buffer_count(sp); - } while(0); mutex_unlock(&sp->mutex); + } while(0); + mutex_unlock(&sp->mutex); return ret; } @@ -590,7 +594,8 @@ int pvr2_stream_get_ready_count(struct pvr2_stream *sp) void pvr2_stream_kill(struct pvr2_stream *sp) { struct pvr2_buffer *bp; - mutex_lock(&sp->mutex); do { + mutex_lock(&sp->mutex); + do { pvr2_stream_internal_flush(sp); while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { pvr2_buffer_set_idle(bp); @@ -598,7 +603,8 @@ void pvr2_stream_kill(struct pvr2_stream *sp) if (sp->buffer_total_count != sp->buffer_target_count) { pvr2_stream_achieve_buffer_count(sp); } - } while(0); mutex_unlock(&sp->mutex); + } while(0); + mutex_unlock(&sp->mutex); } int pvr2_buffer_queue(struct pvr2_buffer *bp) @@ -612,7 +618,8 @@ int pvr2_buffer_queue(struct pvr2_buffer *bp) struct pvr2_stream *sp; if (!bp) return -EINVAL; sp = bp->stream; - mutex_lock(&sp->mutex); do { + mutex_lock(&sp->mutex); + do { pvr2_buffer_wipe(bp); if (!sp->dev) { ret = -EIO; @@ -636,7 +643,8 @@ int pvr2_buffer_queue(struct pvr2_buffer *bp) buffer_complete, bp); usb_submit_urb(bp->purb,GFP_KERNEL); - } while(0); mutex_unlock(&sp->mutex); + } while(0); + mutex_unlock(&sp->mutex); return ret; } @@ -647,7 +655,8 @@ int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) struct pvr2_stream *sp; if (!bp) return -EINVAL; sp = bp->stream; - mutex_lock(&sp->mutex); do { + mutex_lock(&sp->mutex); + do { spin_lock_irqsave(&sp->list_lock,irq_flags); if (bp->state != pvr2_buffer_state_idle) { ret = -EPERM; @@ -664,7 +673,8 @@ int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) bp->stream->i_bcount,bp->stream->i_count); } spin_unlock_irqrestore(&sp->list_lock,irq_flags); - } while(0); mutex_unlock(&sp->mutex); + } while(0); + mutex_unlock(&sp->mutex); return ret; } diff --git a/kernel/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/kernel/drivers/media/usb/pvrusb2/pvrusb2-ioread.c index cd995b547..614d55767 100644 --- a/kernel/drivers/media/usb/pvrusb2/pvrusb2-ioread.c +++ b/kernel/drivers/media/usb/pvrusb2/pvrusb2-ioread.c @@ -205,7 +205,8 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) unsigned int idx; struct pvr2_buffer *bp; - mutex_lock(&cp->mutex); do { + mutex_lock(&cp->mutex); + do { if (cp->stream) { pvr2_trace(PVR2_TRACE_START_STOP, "/*---TRACE_READ---*/" @@ -235,7 +236,8 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) } cp->stream = sp; } - } while (0); mutex_unlock(&cp->mutex); + } while (0); + mutex_unlock(&cp->mutex); return 0; } @@ -245,13 +247,15 @@ int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) int ret = 0; if ((!fl) == (!(cp->enabled))) return ret; - mutex_lock(&cp->mutex); do { + mutex_lock(&cp->mutex); + do { if (fl) { ret = pvr2_ioread_start(cp); } else { pvr2_ioread_stop(cp); } - } while (0); mutex_unlock(&cp->mutex); + } while (0); + mutex_unlock(&cp->mutex); return ret; } @@ -315,7 +319,8 @@ static void pvr2_ioread_filter(struct pvr2_ioread *cp) // Search the stream for our synchronization key. This is made // complicated by the fact that in order to be honest with // ourselves here we must search across buffer boundaries... - mutex_lock(&cp->mutex); while (1) { + mutex_lock(&cp->mutex); + while (1) { // Ensure we have a buffer if (!pvr2_ioread_get_buffer(cp)) break; if (!cp->c_data_len) break; @@ -362,7 +367,8 @@ static void pvr2_ioread_filter(struct pvr2_ioread *cp) } continue; // (for clarity) - } mutex_unlock(&cp->mutex); + } + mutex_unlock(&cp->mutex); } int pvr2_ioread_avail(struct pvr2_ioread *cp) @@ -422,7 +428,8 @@ int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) cp->stream_running = !0; - mutex_lock(&cp->mutex); do { + mutex_lock(&cp->mutex); + do { // Suck data out of the buffers and copy to the user copied_cnt = 0; @@ -480,7 +487,8 @@ int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) } } - } while (0); mutex_unlock(&cp->mutex); + } while (0); + mutex_unlock(&cp->mutex); if (!ret) { if (copied_cnt) { diff --git a/kernel/drivers/media/usb/pwc/pwc-if.c b/kernel/drivers/media/usb/pwc/pwc-if.c index 702267e20..b79c36fd8 100644 --- a/kernel/drivers/media/usb/pwc/pwc-if.c +++ b/kernel/drivers/media/usb/pwc/pwc-if.c @@ -240,9 +240,9 @@ static void pwc_frame_complete(struct pwc_device *pdev) PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" " discarded.\n", fbuf->filled); } else { - fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE; - fbuf->vb.v4l2_buf.sequence = pdev->vframe_count; - vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + fbuf->vb.field = V4L2_FIELD_NONE; + fbuf->vb.sequence = pdev->vframe_count; + vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); pdev->fill_buf = NULL; pdev->vsync = 0; } @@ -287,7 +287,7 @@ static void pwc_isoc_handler(struct urb *urb) { PWC_ERROR("Too many ISOC errors, bailing out.\n"); if (pdev->fill_buf) { - vb2_buffer_done(&pdev->fill_buf->vb, + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); pdev->fill_buf = NULL; } @@ -317,7 +317,7 @@ static void pwc_isoc_handler(struct urb *urb) if (pdev->vsync == 1) { v4l2_get_timestamp( - &fbuf->vb.v4l2_buf.timestamp); + &fbuf->vb.timestamp); pdev->vsync = 2; } @@ -520,7 +520,7 @@ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev, buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, state); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } @@ -571,7 +571,7 @@ static void pwc_video_release(struct v4l2_device *v) /***************************************************************************/ /* Videobuf2 operations */ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -594,7 +594,9 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int buffer_init(struct vb2_buffer *vb) { - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct pwc_frame_buf *buf = + container_of(vbuf, struct pwc_frame_buf, vb); /* need vmalloc since frame buffer > 128K */ buf->data = vzalloc(PWC_FRAME_SIZE); @@ -618,7 +620,9 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_finish(struct vb2_buffer *vb) { struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct pwc_frame_buf *buf = + container_of(vbuf, struct pwc_frame_buf, vb); if (vb->state == VB2_BUF_STATE_DONE) { /* @@ -633,7 +637,9 @@ static void buffer_finish(struct vb2_buffer *vb) static void buffer_cleanup(struct vb2_buffer *vb) { - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct pwc_frame_buf *buf = + container_of(vbuf, struct pwc_frame_buf, vb); vfree(buf->data); } @@ -641,12 +647,14 @@ static void buffer_cleanup(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); - struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct pwc_frame_buf *buf = + container_of(vbuf, struct pwc_frame_buf, vb); unsigned long flags = 0; /* Check the device has not disconnected between prep and queuing */ if (!pdev->udev) { - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); return; } @@ -695,7 +703,8 @@ static void stop_streaming(struct vb2_queue *vq) pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_ERROR); if (pdev->fill_buf) - vb2_buffer_done(&pdev->fill_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, + VB2_BUF_STATE_ERROR); mutex_unlock(&pdev->v4l2_lock); } diff --git a/kernel/drivers/media/usb/pwc/pwc-uncompress.c b/kernel/drivers/media/usb/pwc/pwc-uncompress.c index b65903fbc..98c46f93f 100644 --- a/kernel/drivers/media/usb/pwc/pwc-uncompress.c +++ b/kernel/drivers/media/usb/pwc/pwc-uncompress.c @@ -40,7 +40,7 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) u16 *src; u16 *dsty, *dstu, *dstv; - image = vb2_plane_vaddr(&fbuf->vb, 0); + image = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0); yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ @@ -55,12 +55,12 @@ int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf) * determine this using the type of the webcam */ memcpy(raw_frame->cmd, pdev->cmd_buf, 4); memcpy(raw_frame+1, yuv, pdev->frame_size); - vb2_set_plane_payload(&fbuf->vb, 0, + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, pdev->frame_size + sizeof(struct pwc_raw_frame)); return 0; } - vb2_set_plane_payload(&fbuf->vb, 0, + vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, pdev->width * pdev->height * 3 / 2); if (pdev->vbandlength == 0) { diff --git a/kernel/drivers/media/usb/pwc/pwc.h b/kernel/drivers/media/usb/pwc/pwc.h index 81b017a55..3c73bdaae 100644 --- a/kernel/drivers/media/usb/pwc/pwc.h +++ b/kernel/drivers/media/usb/pwc/pwc.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include @@ -210,7 +211,8 @@ struct pwc_raw_frame { /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { - struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; struct list_head list; void *data; int filled; /* number of bytes filled */ diff --git a/kernel/drivers/media/usb/s2255/s2255drv.c b/kernel/drivers/media/usb/s2255/s2255drv.c index 0f3c34d47..e7acb12ad 100644 --- a/kernel/drivers/media/usb/s2255/s2255drv.c +++ b/kernel/drivers/media/usb/s2255/s2255drv.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -293,7 +294,7 @@ struct s2255_fmt { /* buffer for one video frame */ struct s2255_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -573,14 +574,14 @@ static void s2255_got_frame(struct s2255_vc *vc, int jpgsize) buf = list_entry(vc->buf_list.next, struct s2255_buffer, list); list_del(&buf->list); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - buf->vb.v4l2_buf.field = vc->field; - buf->vb.v4l2_buf.sequence = vc->frame_count; + v4l2_get_timestamp(&buf->vb.timestamp); + buf->vb.field = vc->field; + buf->vb.sequence = vc->frame_count; spin_unlock_irqrestore(&vc->qlock, flags); s2255_fillbuff(vc, buf, jpgsize); /* tell v4l buffer was filled */ - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf); } @@ -612,7 +613,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, { int pos = 0; const char *tmpbuf; - char *vbuf = vb2_plane_vaddr(&buf->vb, 0); + char *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); unsigned long last_frame; struct s2255_dev *dev = vc->dev; @@ -635,7 +636,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: - vb2_set_plane_payload(&buf->vb, 0, jpgsize); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; case V4L2_PIX_FMT_YUV422P: @@ -659,7 +660,7 @@ static void s2255_fillbuff(struct s2255_vc *vc, Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -674,7 +675,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int buffer_prepare(struct vb2_buffer *vb) { struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb); int w = vc->width; int h = vc->height; unsigned long size; @@ -696,13 +698,14 @@ static int buffer_prepare(struct vb2_buffer *vb) return -EINVAL; } - vb2_set_plane_payload(&buf->vb, 0, size); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); return 0; } static void buffer_queue(struct vb2_buffer *vb) { - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb); struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); unsigned long flags = 0; dprintk(vc->dev, 1, "%s\n", __func__); @@ -1116,9 +1119,9 @@ static void stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&vc->qlock, flags); list_for_each_entry_safe(buf, node, &vc->buf_list, list) { list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dprintk(vc->dev, 2, "[%p/%d] done\n", - buf, buf->vb.v4l2_buf.index); + buf, buf->vb.vb2_buf.index); } spin_unlock_irqrestore(&vc->qlock, flags); } diff --git a/kernel/drivers/media/usb/stk1160/stk1160-core.c b/kernel/drivers/media/usb/stk1160/stk1160-core.c index 03504dcf3..1b6836f15 100644 --- a/kernel/drivers/media/usb/stk1160/stk1160-core.c +++ b/kernel/drivers/media/usb/stk1160/stk1160-core.c @@ -162,7 +162,7 @@ static void stk1160_release(struct v4l2_device *v4l2_dev) { struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev); - stk1160_info("releasing all resources\n"); + stk1160_dbg("releasing all resources\n"); stk1160_i2c_unregister(dev); @@ -363,9 +363,6 @@ static int stk1160_probe(struct usb_interface *interface, dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "saa7115_auto", 0, saa7113_addrs); - stk1160_info("driver ver %s successfully loaded\n", - STK1160_VERSION); - /* i2c reset saa711x */ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); diff --git a/kernel/drivers/media/usb/stk1160/stk1160-reg.h b/kernel/drivers/media/usb/stk1160/stk1160-reg.h index 3e49da6e7..81ff3a15d 100644 --- a/kernel/drivers/media/usb/stk1160/stk1160-reg.h +++ b/kernel/drivers/media/usb/stk1160/stk1160-reg.h @@ -33,6 +33,40 @@ */ #define STK1160_DCTRL 0x100 +/* + * Decimation Control Register: + * Byte 104: Horizontal Decimation Line Unit Count + * Byte 105: Vertical Decimation Line Unit Count + * Byte 106: Decimation Control + * Bit 0 - Horizontal Decimation Control + * 0 Horizontal decimation is disabled. + * 1 Horizontal decimation is enabled. + * Bit 1 - Decimates Half or More Column + * 0 Decimates less than half from original column, + * send count unit (0x105) before each unit skipped. + * 1 Decimates half or more from original column, + * skip count unit (0x105) before each unit sent. + * Bit 2 - Vertical Decimation Control + * 0 Vertical decimation is disabled. + * 1 Vertical decimation is enabled. + * Bit 3 - Vertical Greater or Equal to Half + * 0 Decimates less than half from original row, + * send count unit (0x105) before each unit skipped. + * 1 Decimates half or more from original row, + * skip count unit (0x105) before each unit sent. + * Bit 4 - Decimation Unit + * 0 Decimation will work with 2 rows or columns per unit. + * 1 Decimation will work with 4 rows or columns per unit. + */ +#define STK1160_DMCTRL_H_UNITS 0x104 +#define STK1160_DMCTRL_V_UNITS 0x105 +#define STK1160_DMCTRL 0x106 +#define STK1160_H_DEC_EN BIT(0) +#define STK1160_H_DEC_MODE BIT(1) +#define STK1160_V_DEC_EN BIT(2) +#define STK1160_V_DEC_MODE BIT(3) +#define STK1160_DEC_UNIT_SIZE BIT(4) + /* Capture Frame Start Position */ #define STK116_CFSPO 0x110 #define STK116_CFSPO_STX_L 0x110 diff --git a/kernel/drivers/media/usb/stk1160/stk1160-v4l.c b/kernel/drivers/media/usb/stk1160/stk1160-v4l.c index 749ad5603..0bd34f1e7 100644 --- a/kernel/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/kernel/drivers/media/usb/stk1160/stk1160-v4l.c @@ -42,6 +42,17 @@ static bool keep_buffers; module_param(keep_buffers, bool, 0644); MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); +enum stk1160_decimate_mode { + STK1160_DECIMATE_MORE_THAN_HALF, + STK1160_DECIMATE_LESS_THAN_HALF, +}; + +struct stk1160_decimate_ctrl { + bool col_en, row_en; + enum stk1160_decimate_mode col_mode, row_mode; + unsigned int col_n, row_n; +}; + /* supported video standards */ static struct stk1160_fmt format[] = { { @@ -51,6 +62,19 @@ static struct stk1160_fmt format[] = { } }; +/* + * Helper to find the next divisor that results in modulo being zero. + * This is required to guarantee valid decimation unit counts. + */ +static unsigned int +div_round_integer(unsigned int x, unsigned int y) +{ + for (;; y++) { + if (x % y == 0) + return x / y; + } +} + static void stk1160_set_std(struct stk1160 *dev) { int i; @@ -106,6 +130,41 @@ static void stk1160_set_std(struct stk1160 *dev) } +static void stk1160_set_fmt(struct stk1160 *dev, + struct stk1160_decimate_ctrl *ctrl) +{ + u32 val = 0; + + if (ctrl) { + /* + * Since the format is UYVY, the device must skip or send + * a number of rows/columns multiple of four. This way, the + * colour format is preserved. The STK1160_DEC_UNIT_SIZE bit + * does exactly this. + */ + val |= STK1160_DEC_UNIT_SIZE; + val |= ctrl->col_en ? STK1160_H_DEC_EN : 0; + val |= ctrl->row_en ? STK1160_V_DEC_EN : 0; + val |= ctrl->col_mode == + STK1160_DECIMATE_MORE_THAN_HALF ? + STK1160_H_DEC_MODE : 0; + val |= ctrl->row_mode == + STK1160_DECIMATE_MORE_THAN_HALF ? + STK1160_V_DEC_MODE : 0; + + /* Horizontal count units */ + stk1160_write_reg(dev, STK1160_DMCTRL_H_UNITS, ctrl->col_n); + /* Vertical count units */ + stk1160_write_reg(dev, STK1160_DMCTRL_V_UNITS, ctrl->row_n); + + stk1160_dbg("decimate 0x%x, column units %d, row units %d\n", + val, ctrl->col_n, ctrl->row_n); + } + + /* Decimation control */ + stk1160_write_reg(dev, STK1160_DMCTRL, val); +} + /* * Set a new alternate setting. * Returns true is dev->max_pkt_size has changed, false otherwise. @@ -136,7 +195,7 @@ static bool stk1160_set_alternate(struct stk1160 *dev) dev->alt = i; } - stk1160_info("setting alternate %d\n", dev->alt); + stk1160_dbg("setting alternate %d\n", dev->alt); if (dev->alt != prev_alt) { stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", @@ -194,6 +253,8 @@ static int stk1160_start_streaming(struct stk1160 *dev) /* Start saa711x */ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + dev->sequence = 0; + /* Start stk1160 */ stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); @@ -224,7 +285,7 @@ static void stk1160_stop_hw(struct stk1160 *dev) /* set alternate 0 */ dev->alt = 0; - stk1160_info("setting alternate %d\n", dev->alt); + stk1160_dbg("setting alternate %d\n", dev->alt); usb_set_interface(dev->udev, 0, 0); /* Stop stk1160 */ @@ -321,41 +382,134 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int stk1160_try_fmt(struct stk1160 *dev, struct v4l2_format *f, + struct stk1160_decimate_ctrl *ctrl) { - struct stk1160 *dev = video_drvdata(file); + unsigned int width, height; + unsigned int base_width, base_height; + unsigned int col_n, row_n; + enum stk1160_decimate_mode col_mode, row_mode; + bool col_en, row_en; + + base_width = 720; + base_height = (dev->norm & V4L2_STD_525_60) ? 480 : 576; + + /* Minimum width and height is 5% the frame size */ + width = clamp_t(unsigned int, f->fmt.pix.width, + base_width / 20, base_width); + height = clamp_t(unsigned int, f->fmt.pix.height, + base_height / 20, base_height); + + /* Let's set default no decimation values */ + col_n = 0; + row_n = 0; + col_en = false; + row_en = false; + f->fmt.pix.width = base_width; + f->fmt.pix.height = base_height; + row_mode = STK1160_DECIMATE_LESS_THAN_HALF; + col_mode = STK1160_DECIMATE_LESS_THAN_HALF; + + if (width < base_width && width > base_width / 2) { + /* + * The device will send count units for each + * unit skipped. This means count unit is: + * + * n = width / (frame width - width) + * + * And the width is: + * + * width = (n / n + 1) * frame width + */ + col_n = div_round_integer(width, base_width - width); + if (col_n > 0 && col_n <= 255) { + col_en = true; + col_mode = STK1160_DECIMATE_LESS_THAN_HALF; + f->fmt.pix.width = (base_width * col_n) / (col_n + 1); + } - /* - * User can't choose size at his own will, - * so we just return him the current size chosen - * at standard selection. - * TODO: Implement frame scaling? - */ + } else if (width <= base_width / 2) { + + /* + * The device will skip count units for each + * unit sent. This means count is: + * + * n = (frame width / width) - 1 + * + * And the width is: + * + * width = frame width / (n + 1) + */ + col_n = div_round_integer(base_width, width) - 1; + if (col_n > 0 && col_n <= 255) { + col_en = true; + col_mode = STK1160_DECIMATE_MORE_THAN_HALF; + f->fmt.pix.width = base_width / (col_n + 1); + } + } + + if (height < base_height && height > base_height / 2) { + row_n = div_round_integer(height, base_height - height); + if (row_n > 0 && row_n <= 255) { + row_en = true; + row_mode = STK1160_DECIMATE_LESS_THAN_HALF; + f->fmt.pix.height = (base_height * row_n) / (row_n + 1); + } + + } else if (height <= base_height / 2) { + row_n = div_round_integer(base_height, height) - 1; + if (row_n > 0 && row_n <= 255) { + row_en = true; + row_mode = STK1160_DECIMATE_MORE_THAN_HALF; + f->fmt.pix.height = base_height / (row_n + 1); + } + } f->fmt.pix.pixelformat = dev->fmt->fourcc; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.bytesperline = dev->width * 2; - f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + if (ctrl) { + ctrl->col_en = col_en; + ctrl->col_n = col_n; + ctrl->col_mode = col_mode; + ctrl->row_en = row_en; + ctrl->row_n = row_n; + ctrl->row_mode = row_mode; + } + + stk1160_dbg("width %d, height %d\n", + f->fmt.pix.width, f->fmt.pix.height); return 0; } +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stk1160 *dev = video_drvdata(file); + + return stk1160_try_fmt(dev, f, NULL); +} + static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct stk1160 *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; + struct stk1160_decimate_ctrl ctrl; + int rc; if (vb2_is_busy(q)) return -EBUSY; - vidioc_try_fmt_vid_cap(file, priv, f); - - /* We don't support any format changes */ + rc = stk1160_try_fmt(dev, f, &ctrl); + if (rc < 0) + return rc; + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + stk1160_set_fmt(dev, &ctrl); return 0; } @@ -391,22 +545,15 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) return -ENODEV; /* We need to set this now, before we call stk1160_set_std */ + dev->width = 720; + dev->height = (norm & V4L2_STD_525_60) ? 480 : 576; dev->norm = norm; - /* This is taken from saa7115 video decoder */ - if (dev->norm & V4L2_STD_525_60) { - dev->width = 720; - dev->height = 480; - } else if (dev->norm & V4L2_STD_625_50) { - dev->width = 720; - dev->height = 576; - } else { - stk1160_err("invalid standard\n"); - return -EINVAL; - } - stk1160_set_std(dev); + /* Calling with NULL disables frame decimation */ + stk1160_set_fmt(dev, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); @@ -500,6 +647,7 @@ static const struct v4l2_ioctl_ops stk1160_ioctl_ops = { .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, @@ -516,7 +664,7 @@ static const struct v4l2_ioctl_ops stk1160_ioctl_ops = { /* * Videobuf2 operations */ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, +static int queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -537,8 +685,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, sizes[0] = size; - stk1160_info("%s: buffer count %d, each %ld bytes\n", - __func__, *nbuffers, size); + stk1160_dbg("%s: buffer count %d, each %ld bytes\n", + __func__, *nbuffers, size); return 0; } @@ -547,8 +695,9 @@ static void buffer_queue(struct vb2_buffer *vb) { unsigned long flags; struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct stk1160_buffer *buf = - container_of(vb, struct stk1160_buffer, vb); + container_of(vbuf, struct stk1160_buffer, vb); spin_lock_irqsave(&dev->buf_lock, flags); if (!dev->udev) { @@ -556,7 +705,7 @@ static void buffer_queue(struct vb2_buffer *vb) * If the device is disconnected return the buffer to userspace * directly. The next QBUF call will fail with -ENODEV. */ - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } else { buf->mem = vb2_plane_vaddr(vb, 0); @@ -569,7 +718,7 @@ static void buffer_queue(struct vb2_buffer *vb) * the buffer to userspace directly. */ if (buf->length < dev->width * dev->height * 2) - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); else list_add_tail(&buf->list, &dev->avail_bufs); @@ -621,9 +770,9 @@ void stk1160_clear_queue(struct stk1160 *dev) buf = list_first_entry(&dev->avail_bufs, struct stk1160_buffer, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - stk1160_info("buffer [%p/%d] aborted\n", - buf, buf->vb.v4l2_buf.index); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + stk1160_dbg("buffer [%p/%d] aborted\n", + buf, buf->vb.vb2_buf.index); } /* It's important to release the current buffer */ @@ -631,9 +780,9 @@ void stk1160_clear_queue(struct stk1160 *dev) buf = dev->isoc_ctl.buf; dev->isoc_ctl.buf = NULL; - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - stk1160_info("buffer [%p/%d] aborted\n", - buf, buf->vb.v4l2_buf.index); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + stk1160_dbg("buffer [%p/%d] aborted\n", + buf, buf->vb.vb2_buf.index); } spin_unlock_irqrestore(&dev->buf_lock, flags); } @@ -645,7 +794,7 @@ int stk1160_vb2_setup(struct stk1160 *dev) q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->drv_priv = dev; q->buf_struct_size = sizeof(struct stk1160_buffer); q->ops = &stk1160_video_qops; diff --git a/kernel/drivers/media/usb/stk1160/stk1160-video.c b/kernel/drivers/media/usb/stk1160/stk1160-video.c index 39f1aae20..75654e676 100644 --- a/kernel/drivers/media/usb/stk1160/stk1160-video.c +++ b/kernel/drivers/media/usb/stk1160/stk1160-video.c @@ -96,15 +96,13 @@ void stk1160_buffer_done(struct stk1160 *dev) { struct stk1160_buffer *buf = dev->isoc_ctl.buf; - dev->field_count++; + buf->vb.sequence = dev->sequence++; + buf->vb.field = V4L2_FIELD_INTERLACED; + buf->vb.vb2_buf.planes[0].bytesused = buf->bytesused; + v4l2_get_timestamp(&buf->vb.timestamp); - buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; - buf->vb.v4l2_buf.bytesused = buf->bytesused; - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - - vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->bytesused); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); dev->isoc_ctl.buf = NULL; } diff --git a/kernel/drivers/media/usb/stk1160/stk1160.h b/kernel/drivers/media/usb/stk1160/stk1160.h index abdea484c..1ed1cc43c 100644 --- a/kernel/drivers/media/usb/stk1160/stk1160.h +++ b/kernel/drivers/media/usb/stk1160/stk1160.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -58,7 +58,6 @@ * new drivers should use. * */ -#define DEBUG #ifdef DEBUG #define stk1160_dbg(fmt, args...) \ printk(KERN_DEBUG "stk1160: " fmt, ## args) @@ -78,7 +77,7 @@ /* Buffer for one video frame */ struct stk1160_buffer { /* common v4l buffer stuff -- must be first */ - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; void *mem; @@ -151,8 +150,7 @@ struct stk1160 { v4l2_std_id norm; /* current norm */ struct stk1160_fmt *fmt; /* selected format */ - unsigned int field_count; /* not sure ??? */ - enum v4l2_field field; /* also not sure :/ */ + unsigned int sequence; /* i2c i/o */ struct i2c_adapter i2c_adap; diff --git a/kernel/drivers/media/usb/tm6000/tm6000-alsa.c b/kernel/drivers/media/usb/tm6000/tm6000-alsa.c index 74e5697d8..e21c7aace 100644 --- a/kernel/drivers/media/usb/tm6000/tm6000-alsa.c +++ b/kernel/drivers/media/usb/tm6000/tm6000-alsa.c @@ -42,7 +42,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); diff --git a/kernel/drivers/media/usb/tm6000/tm6000-video.c b/kernel/drivers/media/usb/tm6000/tm6000-video.c index 77ce9efe1..fa5e8bda2 100644 --- a/kernel/drivers/media/usb/tm6000/tm6000-video.c +++ b/kernel/drivers/media/usb/tm6000/tm6000-video.c @@ -621,7 +621,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) dev->isoc_in.maxsize, size); - if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + if (tm6000_alloc_urb_buffers(dev) < 0) { tm6000_err("cannot allocate memory for urb buffers\n"); /* call free, as some buffers might have been allocated */ @@ -714,8 +714,7 @@ static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) struct tm6000_core *dev = fh->dev; unsigned long flags; - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); /* We used to wait for the buffer to finish here, but this didn't work because, as we were keeping the state as VIDEOBUF_QUEUED, diff --git a/kernel/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/kernel/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index cef7a0009..d52d4a8d3 100644 --- a/kernel/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/kernel/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -111,8 +111,8 @@ struct ttusb { int last_filter; u8 c; /* transaction counter, wraps around... */ - fe_sec_tone_mode_t tone; - fe_sec_voltage_t voltage; + enum fe_sec_tone_mode tone; + enum fe_sec_voltage voltage; int mux_state; // 0..2 - MuxSyncWord, 3 - nMuxPacks, 4 - muxpack u8 mux_npacks; @@ -511,7 +511,8 @@ static int ttusb_update_lnb(struct ttusb *ttusb) return err; } -static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int ttusb_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; @@ -520,7 +521,7 @@ static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) } #ifdef TTUSB_TONE -static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int ttusb_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; diff --git a/kernel/drivers/media/usb/ttusb-dec/ttusb_dec.c b/kernel/drivers/media/usb/ttusb-dec/ttusb_dec.c index 15ab584cf..a5de46f04 100644 --- a/kernel/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/kernel/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -375,8 +375,7 @@ static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data) struct ttusb_dec *dec = priv; dec->audio_filter->feed->cb.ts(data, 188, NULL, 0, - &dec->audio_filter->feed->feed.ts, - DMX_OK); + &dec->audio_filter->feed->feed.ts); return 0; } @@ -386,8 +385,7 @@ static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data) struct ttusb_dec *dec = priv; dec->video_filter->feed->cb.ts(data, 188, NULL, 0, - &dec->video_filter->feed->feed.ts, - DMX_OK); + &dec->video_filter->feed->feed.ts); return 0; } @@ -439,7 +437,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) if (output_pva) { dec->video_filter->feed->cb.ts(pva, length, NULL, 0, - &dec->video_filter->feed->feed.ts, DMX_OK); + &dec->video_filter->feed->feed.ts); return; } @@ -500,7 +498,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) case 0x02: /* MainAudioStream */ if (output_pva) { dec->audio_filter->feed->cb.ts(pva, length, NULL, 0, - &dec->audio_filter->feed->feed.ts, DMX_OK); + &dec->audio_filter->feed->feed.ts); return; } @@ -538,7 +536,7 @@ static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet, if (filter) filter->feed->cb.sec(&packet[2], length - 2, NULL, 0, - &filter->filter, DMX_OK); + &filter->filter); } static void ttusb_dec_process_packet(struct ttusb_dec *dec) @@ -593,14 +591,9 @@ static void ttusb_dec_process_packet(struct ttusb_dec *dec) static void swap_bytes(u8 *b, int length) { - u8 c; - length -= length % 2; - for (; length; b += 2, length -= 2) { - c = *b; - *b = *(b + 1); - *(b + 1) = c; - } + for (; length; b += 2, length -= 2) + swap(*b, *(b + 1)); } static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, @@ -1431,8 +1424,8 @@ static int ttusb_dec_init_stb(struct ttusb_dec *dec) __func__, model); return -ENOENT; } - if (version >= 0x01770000) - dec->can_playback = 1; + if (version >= 0x01770000) + dec->can_playback = 1; } return 0; } diff --git a/kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.c index 9c29552ae..8781335ab 100644 --- a/kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.c +++ b/kernel/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -39,7 +39,7 @@ struct ttusbdecfe_state { static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; @@ -48,7 +48,7 @@ static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe, static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe, - fe_status_t *status) + enum fe_status *status) { struct ttusbdecfe_state* state = fe->demodulator_priv; u8 b[] = { 0x00, 0x00, 0x00, 0x00, @@ -169,7 +169,8 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc } -static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) { struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; @@ -179,7 +180,8 @@ static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t } -static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) { struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; diff --git a/kernel/drivers/media/usb/usbtv/usbtv-video.c b/kernel/drivers/media/usb/usbtv/usbtv-video.c index 9d3525f65..e645c9df2 100644 --- a/kernel/drivers/media/usb/usbtv/usbtv-video.c +++ b/kernel/drivers/media/usb/usbtv/usbtv-video.c @@ -29,7 +29,7 @@ */ #include -#include +#include #include "usbtv.h" @@ -306,7 +306,7 @@ static void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk) /* First available buffer. */ buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list); - frame = vb2_plane_vaddr(&buf->vb, 0); + frame = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); /* Copy the chunk data. */ usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd); @@ -314,17 +314,17 @@ static void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk) /* Last chunk in a frame, signalling an end */ if (odd && chunk_no == usbtv->n_chunks-1) { - int size = vb2_plane_size(&buf->vb, 0); + int size = vb2_plane_size(&buf->vb.vb2_buf, 0); enum vb2_buffer_state state = usbtv->chunks_done == usbtv->n_chunks ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; - buf->vb.v4l2_buf.sequence = usbtv->sequence++; - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); - vb2_set_plane_payload(&buf->vb, 0, size); - vb2_buffer_done(&buf->vb, state); + buf->vb.field = V4L2_FIELD_INTERLACED; + buf->vb.sequence = usbtv->sequence++; + v4l2_get_timestamp(&buf->vb.timestamp); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } @@ -422,7 +422,7 @@ static void usbtv_stop(struct usbtv *usbtv) while (!list_empty(&usbtv->bufs)) { struct usbtv_buf *buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); list_del(&buf->list); } spin_unlock_irqrestore(&usbtv->buflock, flags); @@ -599,23 +599,28 @@ static struct v4l2_file_operations usbtv_fops = { }; static int usbtv_queue_setup(struct vb2_queue *vq, - const struct v4l2_format *v4l_fmt, unsigned int *nbuffers, + const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct usbtv *usbtv = vb2_get_drv_priv(vq); + unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); - if (*nbuffers < 2) - *nbuffers = 2; + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; *nplanes = 1; - sizes[0] = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32); + if (fmt && fmt->fmt.pix.sizeimage < size) + return -EINVAL; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size; return 0; } static void usbtv_buf_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct usbtv *usbtv = vb2_get_drv_priv(vb->vb2_queue); - struct usbtv_buf *buf = container_of(vb, struct usbtv_buf, vb); + struct usbtv_buf *buf = container_of(vbuf, struct usbtv_buf, vb); unsigned long flags; if (usbtv->udev == NULL) { @@ -635,6 +640,7 @@ static int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count) if (usbtv->udev == NULL) return -ENODEV; + usbtv->sequence = 0; return usbtv_start(usbtv); } diff --git a/kernel/drivers/media/usb/usbtv/usbtv.h b/kernel/drivers/media/usb/usbtv/usbtv.h index 968119581..19cb8bf7c 100644 --- a/kernel/drivers/media/usb/usbtv/usbtv.h +++ b/kernel/drivers/media/usb/usbtv/usbtv.h @@ -24,6 +24,7 @@ #include #include +#include #include /* Hardware. */ @@ -61,7 +62,7 @@ struct usbtv_norm_params { /* A single videobuf2 frame buffer. */ struct usbtv_buf { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; diff --git a/kernel/drivers/media/usb/usbvision/usbvision-core.c b/kernel/drivers/media/usb/usbvision/usbvision-core.c index 44b0c28d6..dc3b4d515 100644 --- a/kernel/drivers/media/usb/usbvision/usbvision-core.c +++ b/kernel/drivers/media/usb/usbvision/usbvision-core.c @@ -1367,7 +1367,7 @@ static void usbvision_isoc_irq(struct urb *urb) int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) { int err_code = 0; - unsigned char buffer[1]; + unsigned char *buffer = usbvision->ctrl_urb_buffer; if (!USBVISION_IS_OPERATIONAL(usbvision)) return -1; @@ -1401,10 +1401,12 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; + usbvision->ctrl_urb_buffer[0] = value; err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), USBVISION_OP_CODE, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); + USB_RECIP_ENDPOINT, 0, (__u16) reg, + usbvision->ctrl_urb_buffer, 1, HZ); if (err_code < 0) { dev_err(&usbvision->dev->dev, @@ -1596,7 +1598,7 @@ static int usbvision_init_webcam(struct usb_usbvision *usbvision) { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } }; - char value[3]; + unsigned char *value = usbvision->ctrl_urb_buffer; /* the only difference between PAL and NTSC init_values */ if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) @@ -1635,8 +1637,8 @@ static int usbvision_init_webcam(struct usb_usbvision *usbvision) static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) { static const char proc[] = "usbvision_set_video_format"; + unsigned char *value = usbvision->ctrl_urb_buffer; int rc; - unsigned char value[2]; if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; @@ -1677,7 +1679,7 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, int err_code = 0; int usb_width, usb_height; unsigned int frame_rate = 0, frame_drop = 0; - unsigned char value[4]; + unsigned char *value = usbvision->ctrl_urb_buffer; if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; @@ -1789,10 +1791,6 @@ int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames usbvision->num_frames--; } - spin_lock_init(&usbvision->queue_lock); - init_waitqueue_head(&usbvision->wait_frame); - init_waitqueue_head(&usbvision->wait_stream); - /* Allocate all buffers */ for (i = 0; i < usbvision->num_frames; i++) { usbvision->frame[i].index = i; @@ -1872,7 +1870,7 @@ static int usbvision_set_compress_params(struct usb_usbvision *usbvision) { static const char proc[] = "usbvision_set_compresion_params: "; int rc; - unsigned char value[6]; + unsigned char *value = usbvision->ctrl_urb_buffer; value[0] = 0x0F; /* Intra-Compression cycle */ value[1] = 0x01; /* Reg.45 one line per strip */ @@ -1946,7 +1944,7 @@ int usbvision_set_input(struct usb_usbvision *usbvision) { static const char proc[] = "usbvision_set_input: "; int rc; - unsigned char value[8]; + unsigned char *value = usbvision->ctrl_urb_buffer; unsigned char dvi_yuv_value; if (!USBVISION_IS_OPERATIONAL(usbvision)) @@ -2062,8 +2060,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision) static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) { + unsigned char *value = usbvision->ctrl_urb_buffer; int rc; - unsigned char value[8]; if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) { value[0] = 0x42; @@ -2160,55 +2158,6 @@ int usbvision_power_on(struct usb_usbvision *usbvision) } -/* - * usbvision timer stuff - */ - -/* to call usbvision_power_off from task queue */ -static void call_usbvision_power_off(struct work_struct *work) -{ - struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, power_off_work); - - PDEBUG(DBG_FUNC, ""); - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return; - - if (usbvision->user == 0) { - usbvision_i2c_unregister(usbvision); - - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - mutex_unlock(&usbvision->v4l2_lock); -} - -static void usbvision_power_off_timer(unsigned long data) -{ - struct usb_usbvision *usbvision = (void *)data; - - PDEBUG(DBG_FUNC, ""); - del_timer(&usbvision->power_off_timer); - INIT_WORK(&usbvision->power_off_work, call_usbvision_power_off); - (void) schedule_work(&usbvision->power_off_work); -} - -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision) -{ - setup_timer(&usbvision->power_off_timer, usbvision_power_off_timer, - (unsigned long)usbvision); -} - -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision) -{ - mod_timer(&usbvision->power_off_timer, jiffies + USBVISION_POWEROFF_TIME); -} - -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision) -{ - if (timer_pending(&usbvision->power_off_timer)) - del_timer(&usbvision->power_off_timer); -} - /* * usbvision_begin_streaming() * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no @@ -2390,8 +2339,8 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) /* Submit all URBs */ for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { - err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb, - GFP_KERNEL); + err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb, + GFP_KERNEL); if (err_code) { dev_err(&usbvision->dev->dev, "%s: usb_submit_urb(%d) failed: error %d\n", diff --git a/kernel/drivers/media/usb/usbvision/usbvision-i2c.c b/kernel/drivers/media/usb/usbvision/usbvision-i2c.c index 26dbcb114..120de2e02 100644 --- a/kernel/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/kernel/drivers/media/usb/usbvision/usbvision-i2c.c @@ -343,7 +343,7 @@ static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, { int rc, retries; int i; - unsigned char value[6]; + unsigned char *value = usbvision->ctrl_urb_buffer; unsigned char ser_cont; ser_cont = (len & 0x07) | 0x10; diff --git a/kernel/drivers/media/usb/usbvision/usbvision-video.c b/kernel/drivers/media/usb/usbvision/usbvision-video.c index 12b403e78..b693206f6 100644 --- a/kernel/drivers/media/usb/usbvision/usbvision-video.c +++ b/kernel/drivers/media/usb/usbvision/usbvision-video.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -122,8 +123,6 @@ static void usbvision_release(struct usb_usbvision *usbvision); static int isoc_mode = ISOC_MODE_COMPRESS; /* Set the default Debug Mode of the device driver */ static int video_debug; -/* Set the default device to power on at startup */ -static int power_on_at_open = 1; /* Sequential Number of Video Device */ static int video_nr = -1; /* Sequential Number of Radio Device */ @@ -134,13 +133,11 @@ static int radio_nr = -1; /* Showing parameters under SYSFS */ module_param(isoc_mode, int, 0444); module_param(video_debug, int, 0444); -module_param(power_on_at_open, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); -MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)"); MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); @@ -351,11 +348,14 @@ static int usbvision_v4l2_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; - usbvision_reset_power_off_timer(usbvision); - if (usbvision->user) + if (usbvision->user) { err_code = -EBUSY; - else { + } else { + err_code = v4l2_fh_open(file); + if (err_code) + goto unlock; + /* Allocate memory for the scratch ring buffer */ err_code = usbvision_scratch_alloc(usbvision); if (isoc_mode == ISOC_MODE_COMPRESS) { @@ -372,11 +372,6 @@ static int usbvision_v4l2_open(struct file *file) /* If so far no errors then we shall start the camera */ if (!err_code) { - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - /* Send init sequence only once, it's large! */ if (!usbvision->initialized) { int setup_ok = 0; @@ -392,18 +387,14 @@ static int usbvision_v4l2_open(struct file *file) err_code = usbvision_init_isoc(usbvision); /* device must be initialized before isoc transfer */ usbvision_muxsel(usbvision, 0); + + /* prepare queues */ + usbvision_empty_framequeues(usbvision); usbvision->user++; - } else { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } } } - /* prepare queues */ - usbvision_empty_framequeues(usbvision); +unlock: mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); @@ -435,23 +426,16 @@ static int usbvision_v4l2_close(struct file *file) usbvision_scratch_free(usbvision); usbvision->user--; - - if (power_on_at_open) { - /* power off in a little while - to avoid off/on every close/open short sequences */ - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } + mutex_unlock(&usbvision->v4l2_lock); if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); return 0; } - mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); - return 0; + return v4l2_fh_release(file); } @@ -503,18 +487,24 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *vc) { struct usb_usbvision *usbvision = video_drvdata(file); + struct video_device *vdev = video_devdata(file); strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); strlcpy(vc->card, usbvision_device_data[usbvision->dev_model].model_string, sizeof(vc->card)); usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); - vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - vc->capabilities = vc->device_caps | V4L2_CAP_DEVICE_CAPS; + vc->device_caps = usbvision->have_tuner ? V4L2_CAP_TUNER : 0; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + vc->device_caps |= V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + else + vc->device_caps |= V4L2_CAP_RADIO; + + vc->capabilities = vc->device_caps | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (usbvision_device_data[usbvision->dev_model].radio) + vc->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -540,7 +530,6 @@ static int vidioc_enum_input(struct file *file, void *priv, } else { strcpy(vi->name, "Television"); vi->type = V4L2_INPUT_TYPE_TUNER; - vi->audioset = 1; vi->tuner = chan; vi->std = USBVISION_NORMS; } @@ -551,7 +540,7 @@ static int vidioc_enum_input(struct file *file, void *priv, strcpy(vi->name, "Green Video Input"); else strcpy(vi->name, "Composite Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; case 2: vi->type = V4L2_INPUT_TYPE_CAMERA; @@ -559,12 +548,12 @@ static int vidioc_enum_input(struct file *file, void *priv, strcpy(vi->name, "Yellow Video Input"); else strcpy(vi->name, "S-Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; case 3: vi->type = V4L2_INPUT_TYPE_CAMERA; strcpy(vi->name, "Red Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; } return 0; @@ -619,14 +608,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */ + if (vt->index) /* Only tuner 0 */ return -EINVAL; - if (usbvision->radio) { + if (vt->type == V4L2_TUNER_RADIO) strcpy(vt->name, "Radio"); - vt->type = V4L2_TUNER_RADIO; - } else { + else strcpy(vt->name, "Television"); - } + /* Let clients fill in the remainder of this struct */ call_all(usbvision, tuner, g_tuner, vt); @@ -638,8 +626,8 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || vt->index) + /* Only one tuner for now */ + if (vt->index) return -EINVAL; /* let clients handle this */ call_all(usbvision, tuner, s_tuner, vt); @@ -652,12 +640,13 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - freq->tuner = 0; /* Only one tuner */ - if (usbvision->radio) - freq->type = V4L2_TUNER_RADIO; + /* Only one tuner */ + if (freq->tuner) + return -EINVAL; + if (freq->type == V4L2_TUNER_RADIO) + freq->frequency = usbvision->radio_freq; else - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = usbvision->freq; + freq->frequency = usbvision->tv_freq; return 0; } @@ -666,68 +655,22 @@ static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *freq) { struct usb_usbvision *usbvision = video_drvdata(file); + struct v4l2_frequency new_freq = *freq; - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || freq->tuner) + /* Only one tuner for now */ + if (freq->tuner) return -EINVAL; - usbvision->freq = freq->frequency; call_all(usbvision, tuner, s_frequency, freq); - - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (usbvision->radio) - strcpy(a->name, "Radio"); + call_all(usbvision, tuner, g_frequency, &new_freq); + if (freq->type == V4L2_TUNER_RADIO) + usbvision->radio_freq = new_freq.frequency; else - strcpy(a->name, "TV"); + usbvision->tv_freq = new_freq.frequency; return 0; } -static int vidioc_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - if (a->index) - return -EINVAL; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, queryctrl, ctrl); - - if (!ctrl->type) - return -EINVAL; - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, g_ctrl, ctrl); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, s_ctrl, ctrl); - return 0; -} - static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *vr) { @@ -937,6 +880,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, vf->fmt.pix.bytesperline = vf->fmt.pix.width* usbvision->palette.bytes_per_pixel; vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ return 0; } @@ -1061,13 +1006,24 @@ static ssize_t usbvision_read(struct file *file, char __user *buf, __func__, (unsigned long)count, frame->bytes_read); - /* For now, forget the frame if it has not been read in one shot. */ -/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */ +#if 1 + /* + * FIXME: + * For now, forget the frame if it has not been read in one shot. + */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + frame->grabstate = frame_state_unused; +#else + if (frame->bytes_read >= frame->scanlength) { + /* All data has been read */ frame->bytes_read = 0; /* Mark it as available to be used again. */ frame->grabstate = frame_state_unused; -/* } */ + } +#endif return count; } @@ -1156,20 +1112,15 @@ static int usbvision_radio_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; + err_code = v4l2_fh_open(file); + if (err_code) + goto out; if (usbvision->user) { dev_err(&usbvision->rdev.dev, "%s: Someone tried to open an already opened USBVision Radio!\n", __func__); err_code = -EBUSY; } else { - if (power_on_at_open) { - usbvision_reset_power_off_timer(usbvision); - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - } - /* Alternate interface 1 is is the biggest frame size */ err_code = usbvision_set_alternate(usbvision); if (err_code < 0) { @@ -1184,14 +1135,6 @@ static int usbvision_radio_open(struct file *file) usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } - - if (err_code) { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } out: mutex_unlock(&usbvision->v4l2_lock); return err_code; @@ -1201,34 +1144,29 @@ out: static int usbvision_radio_close(struct file *file) { struct usb_usbvision *usbvision = video_drvdata(file); - int err_code = 0; PDEBUG(DBG_IO, ""); mutex_lock(&usbvision->v4l2_lock); /* Set packet size to 0 */ usbvision->iface_alt = 0; - err_code = usb_set_interface(usbvision->dev, usbvision->iface, + usb_set_interface(usbvision->dev, usbvision->iface, usbvision->iface_alt); usbvision_audio_off(usbvision); usbvision->radio = 0; usbvision->user--; - if (power_on_at_open) { - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } - if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); + v4l2_fh_release(file); usbvision_release(usbvision); - return err_code; + return 0; } mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); - return err_code; + return v4l2_fh_release(file); } /* Video registration stuff */ @@ -1241,7 +1179,6 @@ static const struct v4l2_file_operations usbvision_fops = { .read = usbvision_v4l2_read, .mmap = usbvision_v4l2_mmap, .unlocked_ioctl = video_ioctl2, -/* .poll = video_poll, */ }; static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { @@ -1259,17 +1196,15 @@ static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -1290,23 +1225,19 @@ static const struct v4l2_file_operations usbvision_radio_fops = { .owner = THIS_MODULE, .open = usbvision_radio_open, .release = usbvision_radio_close, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device usbvision_radio_template = { @@ -1358,9 +1289,17 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) /* register video4linux devices */ static int usbvision_register_video(struct usb_usbvision *usbvision) { + int res = -ENOMEM; + /* Video Device: */ usbvision_vdev_init(usbvision, &usbvision->vdev, &usbvision_video_template, "USBVision Video"); + if (!usbvision->have_tuner) { + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + } if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", @@ -1384,7 +1323,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) "USBVision[%d]: video_register_device() failed\n", usbvision->nr); usbvision_unregister_video(usbvision); - return -1; + return res; } /* @@ -1409,6 +1348,9 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) goto err_free; + if (v4l2_ctrl_handler_init(&usbvision->hdl, 4)) + goto err_unreg; + usbvision->v4l2_dev.ctrl_handler = &usbvision->hdl; mutex_init(&usbvision->v4l2_lock); /* prepare control urb for control messages during interrupts */ @@ -1417,11 +1359,10 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, goto err_unreg; init_waitqueue_head(&usbvision->ctrl_urb_wq); - usbvision_init_power_off_timer(usbvision); - return usbvision; err_unreg: + v4l2_ctrl_handler_free(&usbvision->hdl); v4l2_device_unregister(&usbvision->v4l2_dev); err_free: kfree(usbvision); @@ -1439,8 +1380,6 @@ static void usbvision_release(struct usb_usbvision *usbvision) { PDEBUG(DBG_PROBE, ""); - usbvision_reset_power_off_timer(usbvision); - usbvision->initialized = 0; usbvision_remove_sysfs(&usbvision->vdev); @@ -1449,6 +1388,7 @@ static void usbvision_release(struct usb_usbvision *usbvision) usb_free_urb(usbvision->ctrl_urb); + v4l2_ctrl_handler_free(&usbvision->hdl); v4l2_device_unregister(&usbvision->v4l2_dev); kfree(usbvision); @@ -1476,19 +1416,18 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) } usbvision->tvnorm_id = usbvision_device_data[model].video_norm; - usbvision->video_inputs = usbvision_device_data[model].video_channels; usbvision->ctl_input = 0; + usbvision->radio_freq = 87.5 * 16000; + usbvision->tv_freq = 400 * 16; /* This should be here to make i2c clients to be able to register */ /* first switch off audio */ if (usbvision_device_data[model].audio_channels > 0) usbvision_audio_off(usbvision); - if (!power_on_at_open) { - /* and then power up the noisy tuner */ - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } + /* and then power up the tuner */ + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); } /* @@ -1581,6 +1520,10 @@ static int usbvision_probe(struct usb_interface *intf, usbvision->nr = usbvision_nr++; + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + usbvision->have_tuner = usbvision_device_data[model].tuner; if (usbvision->have_tuner) usbvision->tuner_type = usbvision_device_data[model].tuner_type; @@ -1635,11 +1578,7 @@ static void usbvision_disconnect(struct usb_interface *intf) usbvision_stop_isoc(usbvision); v4l2_device_disconnect(&usbvision->v4l2_dev); - - if (usbvision->power) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - } + usbvision_i2c_unregister(usbvision); usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ usb_put_dev(usbvision->dev); diff --git a/kernel/drivers/media/usb/usbvision/usbvision.h b/kernel/drivers/media/usb/usbvision/usbvision.h index 140a1f675..4f2e4fde3 100644 --- a/kernel/drivers/media/usb/usbvision/usbvision.h +++ b/kernel/drivers/media/usb/usbvision/usbvision.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -357,6 +358,7 @@ extern struct usb_device_id usbvision_table[]; struct usb_usbvision { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct video_device vdev; /* Video Device */ struct video_device rdev; /* Radio Device */ @@ -376,7 +378,8 @@ struct usb_usbvision { int bridge_type; /* NT1003, NT1004, NT1005 */ int radio; int video_inputs; /* # of inputs */ - unsigned long freq; + unsigned long radio_freq; + unsigned long tv_freq; int audio_mute; int audio_channel; int isoc_mode; /* format of video data for the usb isoc-transfer */ @@ -391,8 +394,6 @@ struct usb_usbvision { unsigned char iface_alt; /* Alt settings */ unsigned char vin_reg2_preset; struct mutex v4l2_lock; - struct timer_list power_off_timer; - struct work_struct power_off_work; int power; /* is the device powered on? */ int user; /* user count for exclusive use */ int initialized; /* Had we already sent init sequence? */ @@ -510,9 +511,6 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); int usbvision_set_input(struct usb_usbvision *usbvision); int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision); int usbvision_power_off(struct usb_usbvision *usbvision); int usbvision_power_on(struct usb_usbvision *usbvision); diff --git a/kernel/drivers/media/usb/uvc/uvc_driver.c b/kernel/drivers/media/usb/uvc/uvc_driver.c index 5970dd6a1..d11fd6ac2 100644 --- a/kernel/drivers/media/usb/uvc/uvc_driver.c +++ b/kernel/drivers/media/usb/uvc/uvc_driver.c @@ -32,6 +32,7 @@ #define DRIVER_DESC "USB Video Class driver" unsigned int uvc_clock_param = CLOCK_MONOTONIC; +unsigned int uvc_hw_timestamps_param; unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param = -1; unsigned int uvc_trace_param; @@ -1967,8 +1968,6 @@ static void uvc_disconnect(struct usb_interface *intf) UVC_SC_VIDEOSTREAMING) return; - dev->state |= UVC_DEV_DISCONNECTED; - uvc_unregister_video(dev); } @@ -2080,6 +2079,8 @@ static int uvc_clock_param_set(const char *val, struct kernel_param *kp) module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, &uvc_clock_param, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); +module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); diff --git a/kernel/drivers/media/usb/uvc/uvc_queue.c b/kernel/drivers/media/usb/uvc/uvc_queue.c index 87a19f33e..cfb868a48 100644 --- a/kernel/drivers/media/usb/uvc/uvc_queue.c +++ b/kernel/drivers/media/usb/uvc/uvc_queue.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "uvcvideo.h" @@ -60,7 +61,7 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue, queue); list_del(&buf->queue); buf->state = state; - vb2_buffer_done(&buf->buf, vb2_state); + vb2_buffer_done(&buf->buf.vb2_buf, vb2_state); } } @@ -68,10 +69,11 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue, * videobuf2 queue operations */ -static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, +static int uvc_queue_setup(struct vb2_queue *vq, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_streaming *stream = uvc_queue_to_stream(queue); @@ -89,10 +91,11 @@ static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int uvc_buffer_prepare(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); - if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); return -EINVAL; @@ -105,7 +108,7 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->error = 0; buf->mem = vb2_plane_vaddr(vb, 0); buf->length = vb2_plane_size(vb, 0); - if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) buf->bytesused = 0; else buf->bytesused = vb2_get_plane_payload(vb, 0); @@ -115,8 +118,9 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) static void uvc_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); unsigned long flags; spin_lock_irqsave(&queue->irqlock, flags); @@ -127,7 +131,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) * directly. The next QBUF call will fail with -ENODEV. */ buf->state = UVC_BUF_STATE_ERROR; - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&queue->irqlock, flags); @@ -135,12 +139,13 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) static void uvc_buffer_finish(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); struct uvc_streaming *stream = uvc_queue_to_stream(queue); - struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); if (vb->state == VB2_BUF_STATE_DONE) - uvc_video_clock_update(stream, &vb->v4l2_buf, buf); + uvc_video_clock_update(stream, vbuf, buf); } static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -270,6 +275,18 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) return ret; } +int uvc_export_buffer(struct uvc_video_queue *queue, + struct v4l2_exportbuffer *exp) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_expbuf(&queue->queue, exp); + mutex_unlock(&queue->mutex); + + return ret; +} + int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, int nonblocking) { @@ -386,7 +403,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, buf->error = 0; buf->state = UVC_BUF_STATE_QUEUED; buf->bytesused = 0; - vb2_set_plane_payload(&buf->buf, 0, 0); + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0); return buf; } @@ -400,8 +417,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, spin_unlock_irqrestore(&queue->irqlock, flags); buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; - vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); - vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); return nextbuf; } diff --git a/kernel/drivers/media/usb/uvc/uvc_v4l2.c b/kernel/drivers/media/usb/uvc/uvc_v4l2.c index c4b1ac675..2764f4360 100644 --- a/kernel/drivers/media/usb/uvc/uvc_v4l2.c +++ b/kernel/drivers/media/usb/uvc/uvc_v4l2.c @@ -483,9 +483,6 @@ static int uvc_v4l2_open(struct file *file) uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); stream = video_drvdata(file); - if (stream->dev->state & UVC_DEV_DISCONNECTED) - return -ENODEV; - ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) return ret; @@ -723,6 +720,18 @@ static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) return uvc_queue_buffer(&stream->queue, buf); } +static int uvc_ioctl_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *exp) +{ + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_export_buffer(&stream->queue, exp); +} + static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct uvc_fh *handle = fh; @@ -1478,6 +1487,7 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = { .vidioc_reqbufs = uvc_ioctl_reqbufs, .vidioc_querybuf = uvc_ioctl_querybuf, .vidioc_qbuf = uvc_ioctl_qbuf, + .vidioc_expbuf = uvc_ioctl_expbuf, .vidioc_dqbuf = uvc_ioctl_dqbuf, .vidioc_create_bufs = uvc_ioctl_create_bufs, .vidioc_streamon = uvc_ioctl_streamon, diff --git a/kernel/drivers/media/usb/uvc/uvc_video.c b/kernel/drivers/media/usb/uvc/uvc_video.c index 20ccc9d31..2b276ab77 100644 --- a/kernel/drivers/media/usb/uvc/uvc_video.c +++ b/kernel/drivers/media/usb/uvc/uvc_video.c @@ -119,6 +119,14 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; + /* The "TOSHIBA Web Camera - 5M" Chicony device (04f2:b50b) seems to + * compute the bandwidth on 16 bits and erroneously sign-extend it to + * 32 bits, resulting in a huge bandwidth value. Detect and fix that + * condition by setting the 16 MSBs to 0 when they're all equal to 1. + */ + if ((ctrl->dwMaxPayloadTransferSize & 0xffff0000) == 0xffff0000) + ctrl->dwMaxPayloadTransferSize &= ~0xffff0000; + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) && stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && stream->intf->num_altsetting > 1) { @@ -598,7 +606,7 @@ static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) * timestamp of the sliding window to 1s. */ void uvc_video_clock_update(struct uvc_streaming *stream, - struct v4l2_buffer *v4l2_buf, + struct vb2_v4l2_buffer *vbuf, struct uvc_buffer *buf) { struct uvc_clock *clock = &stream->clock; @@ -615,6 +623,9 @@ void uvc_video_clock_update(struct uvc_streaming *stream, u32 rem; u64 y; + if (!uvc_hw_timestamps_param) + return; + spin_lock_irqsave(&clock->lock, flags); if (clock->count < clock->size) @@ -688,14 +699,14 @@ void uvc_video_clock_update(struct uvc_streaming *stream, stream->dev->name, sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC, - v4l2_buf->timestamp.tv_sec, - (unsigned long)v4l2_buf->timestamp.tv_usec, + vbuf->timestamp.tv_sec, + (unsigned long)vbuf->timestamp.tv_usec, x1, first->host_sof, first->dev_sof, x2, last->host_sof, last->dev_sof, y1, y2); /* Update the V4L2 buffer. */ - v4l2_buf->timestamp.tv_sec = ts.tv_sec; - v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + vbuf->timestamp.tv_sec = ts.tv_sec; + vbuf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; done: spin_unlock_irqrestore(&stream->clock.lock, flags); @@ -1021,10 +1032,10 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, uvc_video_get_ts(&ts); - buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; - buf->buf.v4l2_buf.sequence = stream->sequence; - buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec; - buf->buf.v4l2_buf.timestamp.tv_usec = + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.timestamp.tv_sec = ts.tv_sec; + buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; /* TODO: Handle PTS and SCR. */ @@ -1297,7 +1308,7 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, if (buf->bytesused == stream->queue.buf_used) { stream->queue.buf_used = 0; buf->state = UVC_BUF_STATE_READY; - buf->buf.v4l2_buf.sequence = ++stream->sequence; + buf->buf.sequence = ++stream->sequence; uvc_queue_next_buffer(&stream->queue, buf); stream->last_fid ^= UVC_STREAM_FID; } diff --git a/kernel/drivers/media/usb/uvc/uvcvideo.h b/kernel/drivers/media/usb/uvc/uvcvideo.h index 1b594c203..f0f2391e1 100644 --- a/kernel/drivers/media/usb/uvc/uvcvideo.h +++ b/kernel/drivers/media/usb/uvc/uvcvideo.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include /* -------------------------------------------------------------------------- * UVC constants @@ -354,7 +354,7 @@ enum uvc_buffer_state { }; struct uvc_buffer { - struct vb2_buffer buf; + struct vb2_v4l2_buffer buf; struct list_head queue; enum uvc_buffer_state state; @@ -517,10 +517,6 @@ struct uvc_streaming { } clock; }; -enum uvc_device_state { - UVC_DEV_DISCONNECTED = 1, -}; - struct uvc_device { struct usb_device *udev; struct usb_interface *intf; @@ -529,7 +525,6 @@ struct uvc_device { int intfnum; char name[32]; - enum uvc_device_state state; struct mutex lock; /* Protects users */ unsigned int users; atomic_t nmappings; @@ -598,6 +593,7 @@ extern unsigned int uvc_clock_param; extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; extern unsigned int uvc_timeout_param; +extern unsigned int uvc_hw_timestamps_param; #define uvc_trace(flag, msg...) \ do { \ @@ -635,6 +631,8 @@ extern int uvc_create_buffers(struct uvc_video_queue *queue, struct v4l2_create_buffers *v4l2_cb); extern int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf); +extern int uvc_export_buffer(struct uvc_video_queue *queue, + struct v4l2_exportbuffer *exp); extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf, int nonblocking); extern int uvc_queue_streamon(struct uvc_video_queue *queue, @@ -676,7 +674,7 @@ extern int uvc_probe_video(struct uvc_streaming *stream, extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); void uvc_video_clock_update(struct uvc_streaming *stream, - struct v4l2_buffer *v4l2_buf, + struct vb2_v4l2_buffer *vbuf, struct uvc_buffer *buf); /* Status */ diff --git a/kernel/drivers/media/usb/zr364xx/zr364xx.c b/kernel/drivers/media/usb/zr364xx/zr364xx.c index ca850316d..7433ba5c4 100644 --- a/kernel/drivers/media/usb/zr364xx/zr364xx.c +++ b/kernel/drivers/media/usb/zr364xx/zr364xx.c @@ -377,8 +377,7 @@ static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) { _DBG("%s\n", __func__); - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); videobuf_vmalloc_free(&buf->vb); buf->vb.state = VIDEOBUF_NEEDS_INIT; diff --git a/kernel/drivers/media/v4l2-core/Kconfig b/kernel/drivers/media/v4l2-core/Kconfig index ba7e21a73..9beece008 100644 --- a/kernel/drivers/media/v4l2-core/Kconfig +++ b/kernel/drivers/media/v4l2-core/Kconfig @@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV tristate depends on VIDEOBUF2_CORE +# Used by LED subsystem flash drivers +config V4L2_FLASH_LED_CLASS + tristate "V4L2 flash API for LED flash class devices" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on LEDS_CLASS_FLASH + ---help--- + Say Y here to enable V4L2 flash API support for LED flash + class drivers. + + When in doubt, say N. + # Used by drivers that need Videobuf modules config VIDEOBUF_GEN tristate @@ -73,6 +84,7 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate + select FRAME_VECTOR config VIDEOBUF2_DMA_CONTIG tristate @@ -89,7 +101,7 @@ config VIDEOBUF2_VMALLOC config VIDEOBUF2_DMA_SG tristate - #depends on HAS_DMA + depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS diff --git a/kernel/drivers/media/v4l2-core/Makefile b/kernel/drivers/media/v4l2-core/Makefile index 63d29f275..1dc8bba2b 100644 --- a/kernel/drivers/media/v4l2-core/Makefile +++ b/kernel/drivers/media/v4l2-core/Makefile @@ -13,6 +13,9 @@ endif ifeq ($(CONFIG_OF),y) videodev-objs += v4l2-of.o endif +ifeq ($(CONFIG_TRACEPOINTS),y) + videodev-objs += vb2-trace.o v4l2-trace.o +endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o @@ -22,13 +25,15 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o +obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o + obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o -obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o +obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o videobuf2-v4l2.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o diff --git a/kernel/drivers/media/v4l2-core/tuner-core.c b/kernel/drivers/media/v4l2-core/tuner-core.c index abdcffabc..581e21ad6 100644 --- a/kernel/drivers/media/v4l2-core/tuner-core.c +++ b/kernel/drivers/media/v4l2-core/tuner-core.c @@ -1366,7 +1366,6 @@ MODULE_DEVICE_TABLE(i2c, tuner_id); static struct i2c_driver tuner_driver = { .driver = { - .owner = THIS_MODULE, .name = "tuner", .pm = &tuner_pm_ops, }, diff --git a/kernel/drivers/media/v4l2-core/v4l2-async.c b/kernel/drivers/media/v4l2-core/v4l2-async.c index 85a6a3412..5bada202b 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-async.c +++ b/kernel/drivers/media/v4l2-core/v4l2-async.c @@ -22,10 +22,10 @@ #include #include -static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { #if IS_ENABLED(CONFIG_I2C) - struct i2c_client *client = i2c_verify_client(dev); + struct i2c_client *client = i2c_verify_client(sd->dev); return client && asd->match.i2c.adapter_id == client->adapter->nr && asd->match.i2c.address == client->addr; @@ -34,14 +34,24 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #endif } -static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_devname(struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { - return !strcmp(asd->match.device_name.name, dev_name(dev)); + return !strcmp(asd->match.device_name.name, dev_name(sd->dev)); } -static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - return dev->of_node == asd->match.of.node; + return sd->of_node == asd->match.of.node; +} + +static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +{ + if (!asd->match.custom.match) + /* Match always */ + return true; + + return asd->match.custom.match(sd->dev, asd); } static LIST_HEAD(subdev_list); @@ -51,17 +61,14 @@ static DEFINE_MUTEX(list_lock); static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) { + bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *); struct v4l2_async_subdev *asd; - bool (*match)(struct device *, struct v4l2_async_subdev *); list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ switch (asd->match_type) { case V4L2_ASYNC_MATCH_CUSTOM: - match = asd->match.custom.match; - if (!match) - /* Match always */ - return asd; + match = match_custom; break; case V4L2_ASYNC_MATCH_DEVNAME: match = match_devname; @@ -79,7 +86,7 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * } /* match cannot be NULL here */ - if (match(sd->dev, asd)) + if (match(sd, asd)) return asd; } @@ -266,6 +273,14 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) { struct v4l2_async_notifier *notifier; + /* + * No reference taken. The reference is held by the device + * (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; + mutex_lock(&list_lock); INIT_LIST_HEAD(&sd->async_list); diff --git a/kernel/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/kernel/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index af6354305..327e83ac2 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/kernel/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -147,6 +147,20 @@ static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, return 0; } +static inline int get_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up) +{ + if (copy_from_user(kp, up, sizeof(struct v4l2_sdr_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up) +{ + if (copy_to_user(up, kp, sizeof(struct v4l2_sdr_format))) + return -EFAULT; + return 0; +} + struct v4l2_format32 { __u32 type; /* enum v4l2_buf_type */ union { @@ -155,6 +169,7 @@ struct v4l2_format32 { struct v4l2_window32 win; struct v4l2_vbi_format vbi; struct v4l2_sliced_vbi_format sliced; + struct v4l2_sdr_format sdr; __u8 raw_data[200]; /* user-defined */ } fmt; }; @@ -198,8 +213,11 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_SDR_CAPTURE: + case V4L2_BUF_TYPE_SDR_OUTPUT: + return get_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr); default: - printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", + pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n", kp->type); return -EINVAL; } @@ -242,8 +260,11 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_SDR_CAPTURE: + case V4L2_BUF_TYPE_SDR_OUTPUT: + return put_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr); default: - printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", + pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n", kp->type); return -EINVAL; } @@ -266,7 +287,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_ struct v4l2_standard32 { __u32 index; - __u32 id[2]; /* __u64 would get the alignment wrong */ + compat_u64 id; __u8 name[24]; struct v4l2_fract frameperiod; /* Frames, not fields */ __u32 framelines; @@ -286,7 +307,7 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || put_user(kp->index, &up->index) || - copy_to_user(up->id, &kp->id, sizeof(__u64)) || + put_user(kp->id, &up->id) || copy_to_user(up->name, kp->name, 24) || copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) || put_user(kp->framelines, &up->framelines) || @@ -587,10 +608,10 @@ struct v4l2_input32 { __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ __u32 tuner; /* Associated tuner */ - v4l2_std_id std; + compat_u64 std; __u32 status; __u32 reserved[4]; -} __attribute__ ((packed)); +}; /* The 64-bit v4l2_input struct has extra padding at the end of the struct. Otherwise it is identical to the 32-bit version. */ @@ -609,11 +630,11 @@ static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __ } struct v4l2_ext_controls32 { - __u32 ctrl_class; - __u32 count; - __u32 error_idx; - __u32 reserved[2]; - compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; struct v4l2_ext_control32 { @@ -655,7 +676,8 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext get_user(kp->ctrl_class, &up->ctrl_class) || get_user(kp->count, &up->count) || get_user(kp->error_idx, &up->error_idx) || - copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + copy_from_user(kp->reserved, up->reserved, + sizeof(kp->reserved))) return -EFAULT; n = kp->count; if (n == 0) { @@ -738,6 +760,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext struct v4l2_event32 { __u32 type; union { + compat_s64 value64; __u8 data[64]; } u; __u32 pending; @@ -1033,8 +1056,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) ret = vdev->fops->compat_ioctl32(file, cmd, arg); if (ret == -ENOIOCTLCMD) - pr_warn("compat_ioctl32: unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", - _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd); + pr_debug("compat_ioctl32: unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", + _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd); return ret; } EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32); diff --git a/kernel/drivers/media/v4l2-core/v4l2-ctrls.c b/kernel/drivers/media/v4l2-core/v4l2-ctrls.c index e3a346800..4a1d9fdd1 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/kernel/drivers/media/v4l2-core/v4l2-ctrls.c @@ -888,6 +888,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis"; case V4L2_CID_RDS_RECEPTION: return "RDS Reception"; case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls"; + case V4L2_CID_RF_TUNER_RF_GAIN: return "RF Gain"; case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto"; case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain"; case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: return "Mixer Gain, Auto"; @@ -1161,6 +1162,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_PILOT_TONE_FREQUENCY: case V4L2_CID_TUNE_POWER_LEVEL: case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + case V4L2_CID_RF_TUNER_RF_GAIN: case V4L2_CID_RF_TUNER_LNA_GAIN: case V4L2_CID_RF_TUNER_MIXER_GAIN: case V4L2_CID_RF_TUNER_IF_GAIN: @@ -1678,21 +1680,6 @@ static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) unsigned idx; int err = 0; - if (!ctrl->is_ptr) { - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_INTEGER_MENU: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_BITMASK: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - case V4L2_CTRL_TYPE_INTEGER64: - return ctrl->type_ops->validate(ctrl, 0, p_new); - default: - break; - } - } for (idx = 0; !err && idx < ctrl->elems; idx++) err = ctrl->type_ops->validate(ctrl, idx, p_new); return err; @@ -2513,7 +2500,7 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr /* We found a control with the given ID, so just get the next valid one in the list. */ list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) { - is_compound = + is_compound = ref->ctrl->is_array || ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; if (id < ref->ctrl->id && (is_compound & mask) == match) @@ -2527,7 +2514,7 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr is one, otherwise the first 'if' above would have been true. */ list_for_each_entry(ref, &hdl->ctrl_refs, node) { - is_compound = + is_compound = ref->ctrl->is_array || ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; if (id < ref->ctrl->id && (is_compound & mask) == match) @@ -2899,7 +2886,7 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) * cur_to_user() calls below would need to be modified not to access * userspace memory when called from get_ctrl(). */ - if (!ctrl->is_int) + if (!ctrl->is_int && ctrl->type != V4L2_CTRL_TYPE_INTEGER64) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) @@ -2957,9 +2944,9 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl) /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); - c.value = 0; + c.value64 = 0; get_ctrl(ctrl, &c); - return c.value; + return c.value64; } EXPORT_SYMBOL(v4l2_ctrl_g_ctrl_int64); @@ -3058,7 +3045,7 @@ static void update_from_auto_cluster(struct v4l2_ctrl *master) { int i; - for (i = 0; i < master->ncontrols; i++) + for (i = 1; i < master->ncontrols; i++) cur_to_new(master->cluster[i]); if (!call_op(master, g_volatile_ctrl)) for (i = 1; i < master->ncontrols; i++) diff --git a/kernel/drivers/media/v4l2-core/v4l2-dev.c b/kernel/drivers/media/v4l2-core/v4l2-dev.c index 71a1b93b0..6b1eaeddb 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-dev.c +++ b/kernel/drivers/media/v4l2-core/v4l2-dev.c @@ -637,8 +637,8 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_try_fmt_sliced_vbi_out))) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); - } else if (is_sdr) { - /* SDR specific ioctls */ + } else if (is_sdr && is_rx) { + /* SDR receiver specific ioctls */ if (ops->vidioc_enum_fmt_sdr_cap) set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); if (ops->vidioc_g_fmt_sdr_cap) @@ -647,6 +647,16 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); if (ops->vidioc_try_fmt_sdr_cap) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + } else if (is_sdr && is_tx) { + /* SDR transmitter specific ioctls */ + if (ops->vidioc_enum_fmt_sdr_out) + set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); + if (ops->vidioc_g_fmt_sdr_out) + set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + if (ops->vidioc_s_fmt_sdr_out) + set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + if (ops->vidioc_try_fmt_sdr_out) + set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); } if (is_vid || is_vbi || is_sdr) { diff --git a/kernel/drivers/media/v4l2-core/v4l2-dv-timings.c b/kernel/drivers/media/v4l2-core/v4l2-dv-timings.c index c0e96382f..6a83d6191 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/kernel/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions"); @@ -255,20 +256,25 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, { const struct v4l2_bt_timings *bt = &t->bt; u32 htot, vtot; + u32 fps; if (t->type != V4L2_DV_BT_656_1120) return; htot = V4L2_DV_BT_FRAME_WIDTH(bt); vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + if (bt->interlaced) + vtot /= 2; + + fps = (htot * vtot) > 0 ? div_u64((100 * (u64)bt->pixelclock), + (htot * vtot)) : 0; if (prefix == NULL) prefix = ""; - pr_info("%s: %s%ux%u%s%u (%ux%u)\n", dev_prefix, prefix, + pr_info("%s: %s%ux%u%s%u.%u (%ux%u)\n", dev_prefix, prefix, bt->width, bt->height, bt->interlaced ? "i" : "p", - (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, - htot, vtot); + fps / 100, fps % 100, htot, vtot); if (!detailed) return; @@ -281,10 +287,17 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, dev_prefix, bt->vfrontporch, (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", bt->vsync, bt->vbackporch); + if (bt->interlaced) + pr_info("%s: vertical bottom field: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->il_vfrontporch, + (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", + bt->il_vsync, bt->il_vbackporch); pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); - pr_info("%s: flags (0x%x):%s%s%s%s%s\n", dev_prefix, bt->flags, + pr_info("%s: flags (0x%x):%s%s%s%s%s%s\n", dev_prefix, bt->flags, (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? " REDUCED_BLANKING" : "", + ((bt->flags & V4L2_DV_FL_REDUCED_BLANKING) && + bt->vsync == 8) ? " (V2)" : "", (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? " CAN_REDUCE_FPS" : "", (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? @@ -308,11 +321,13 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); */ #define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ +#define CVT_PXL_CLK_GRAN_RB_V2 1000 /* granularity for reduced blanking v2*/ /* Normal blanking */ #define CVT_MIN_V_BPORCH 7 /* lines */ #define CVT_MIN_V_PORCH_RND 3 /* lines */ #define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ +#define CVT_HSYNC_PERCENT 8 /* nominal hsync as percentage of line */ /* Normal blanking for CVT uses GTF to calculate horizontal blanking */ #define CVT_CELL_GRAN 8 /* character cell granularity */ @@ -326,36 +341,46 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); /* Reduced Blanking */ #define CVT_RB_MIN_V_BPORCH 7 /* lines */ #define CVT_RB_V_FPORCH 3 /* lines */ -#define CVT_RB_MIN_V_BLANK 460 /* us */ +#define CVT_RB_MIN_V_BLANK 460 /* us */ #define CVT_RB_H_SYNC 32 /* pixels */ -#define CVT_RB_H_BPORCH 80 /* pixels */ #define CVT_RB_H_BLANK 160 /* pixels */ +/* Reduce blanking Version 2 */ +#define CVT_RB_V2_H_BLANK 80 /* pixels */ +#define CVT_RB_MIN_V_FPORCH 3 /* lines */ +#define CVT_RB_V2_MIN_V_FPORCH 1 /* lines */ +#define CVT_RB_V_BPORCH 6 /* lines */ /** v4l2_detect_cvt - detect if the given timings follow the CVT standard * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. * @vsync - the height of the vertical sync in lines. + * @active_width - active width of image (does not include blanking). This + * information is needed only in case of version 2 of reduced blanking. + * In other cases, this parameter does not have any effect on timings. * @polarities - the horizontal and vertical polarities (same as struct * v4l2_bt_timings polarities). + * @interlaced - if this flag is true, it indicates interlaced format * @fmt - the resulting timings. * * This function will attempt to detect if the given values correspond to a * valid CVT format. If so, then it will return true, and fmt will be filled * in with the found CVT timings. - * - * TODO: VESA defined a new version 2 of their reduced blanking - * formula. Support for that is currently missing in this CVT - * detection function. */ -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt) +bool v4l2_detect_cvt(unsigned frame_height, + unsigned hfreq, + unsigned vsync, + unsigned active_width, + u32 polarities, + bool interlaced, + struct v4l2_dv_timings *fmt) { int v_fp, v_bp, h_fp, h_bp, hsync; int frame_width, image_height, image_width; bool reduced_blanking; + bool rb_v2 = false; unsigned pix_clk; - if (vsync < 4 || vsync > 7) + if (vsync < 4 || vsync > 8) return false; if (polarities == V4L2_DV_VSYNC_POS_POL) @@ -365,22 +390,50 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, else return false; + if (reduced_blanking && vsync == 8) + rb_v2 = true; + + if (rb_v2 && active_width == 0) + return false; + + if (!rb_v2 && vsync > 7) + return false; + + if (hfreq == 0) + return false; + /* Vertical */ if (reduced_blanking) { - v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 1999999) / 1000000; - v_bp -= vsync + v_fp; - - if (v_bp < CVT_RB_MIN_V_BPORCH) - v_bp = CVT_RB_MIN_V_BPORCH; + if (rb_v2) { + v_bp = CVT_RB_V_BPORCH; + v_fp = (CVT_RB_MIN_V_BLANK * hfreq) / 1000000 + 1; + v_fp -= vsync + v_bp; + + if (v_fp < CVT_RB_V2_MIN_V_FPORCH) + v_fp = CVT_RB_V2_MIN_V_FPORCH; + } else { + v_fp = CVT_RB_V_FPORCH; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq) / 1000000 + 1; + v_bp -= vsync + v_fp; + + if (v_bp < CVT_RB_MIN_V_BPORCH) + v_bp = CVT_RB_MIN_V_BPORCH; + } } else { v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 1999999) / 1000000 - vsync; + v_bp = (CVT_MIN_VSYNC_BP * hfreq) / 1000000 + 1 - vsync; if (v_bp < CVT_MIN_V_BPORCH) v_bp = CVT_MIN_V_BPORCH; } - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (interlaced) + image_height = (frame_height - 2 * v_fp - 2 * vsync - 2 * v_bp) & ~0x1; + else + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (image_height < 0) + return false; /* Aspect ratio based on vsync */ switch (vsync) { @@ -402,22 +455,32 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, else return false; break; + case 8: + image_width = active_width; + break; default: return false; } - image_width = image_width & ~7; + if (!rb_v2) + image_width = image_width & ~7; /* Horizontal */ if (reduced_blanking) { - pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + int h_blank; + int clk_gran; + + h_blank = rb_v2 ? CVT_RB_V2_H_BLANK : CVT_RB_H_BLANK; + clk_gran = rb_v2 ? CVT_PXL_CLK_GRAN_RB_V2 : CVT_PXL_CLK_GRAN; + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = (pix_clk / clk_gran) * clk_gran; - h_bp = CVT_RB_H_BPORCH; + h_bp = h_blank / 2; hsync = CVT_RB_H_SYNC; - h_fp = CVT_RB_H_BLANK - h_bp - hsync; + h_fp = h_blank - h_bp - hsync; - frame_width = image_width + CVT_RB_H_BLANK; + frame_width = image_width + h_blank; } else { unsigned ideal_duty_cycle_per_myriad = 100 * CVT_C_PRIME - (CVT_M_PRIME * 100000) / hfreq; @@ -436,8 +499,8 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, h_bp = h_blank / 2; frame_width = image_width + h_blank; - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % CVT_CELL_GRAN; + hsync = frame_width * CVT_HSYNC_PERCENT / 100; + hsync = (hsync / CVT_CELL_GRAN) * CVT_CELL_GRAN; h_fp = h_blank - hsync - h_bp; } @@ -450,11 +513,27 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, fmt->bt.hsync = hsync; fmt->bt.vsync = vsync; fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + + if (!interlaced) { + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.interlaced = V4L2_DV_PROGRESSIVE; + } else { + fmt->bt.vbackporch = (frame_height - image_height - 2 * v_fp - + 2 * vsync) / 2; + fmt->bt.il_vbackporch = frame_height - image_height - 2 * v_fp - + 2 * vsync - fmt->bt.vbackporch; + fmt->bt.il_vfrontporch = v_fp; + fmt->bt.il_vsync = vsync; + fmt->bt.flags |= V4L2_DV_FL_HALF_LINE; + fmt->bt.interlaced = V4L2_DV_INTERLACED; + } + fmt->bt.pixelclock = pix_clk; fmt->bt.standards = V4L2_DV_BT_STD_CVT; + if (reduced_blanking) fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; } EXPORT_SYMBOL_GPL(v4l2_detect_cvt); @@ -493,6 +572,7 @@ EXPORT_SYMBOL_GPL(v4l2_detect_cvt); * @vsync - the height of the vertical sync in lines. * @polarities - the horizontal and vertical polarities (same as struct * v4l2_bt_timings polarities). + * @interlaced - if this flag is true, it indicates interlaced format * @aspect - preferred aspect ratio. GTF has no method of determining the * aspect ratio in order to derive the image width from the * image height, so it has to be passed explicitly. Usually @@ -508,6 +588,7 @@ bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, u32 polarities, + bool interlaced, struct v4l2_fract aspect, struct v4l2_dv_timings *fmt) { @@ -527,10 +608,19 @@ bool v4l2_detect_gtf(unsigned frame_height, else return false; + if (hfreq == 0) + return false; + /* Vertical */ v_fp = GTF_V_FP; - v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + v_bp = (GTF_MIN_VSYNC_BP * hfreq + 500000) / 1000000 - vsync; + if (interlaced) + image_height = (frame_height - 2 * v_fp - 2 * vsync - 2 * v_bp) & ~0x1; + else + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (image_height < 0) + return false; if (aspect.numerator == 0 || aspect.denominator == 0) { aspect.numerator = 16; @@ -540,25 +630,35 @@ bool v4l2_detect_gtf(unsigned frame_height, image_width = (image_width + GTF_CELL_GRAN/2) & ~(GTF_CELL_GRAN - 1); /* Horizontal */ - if (default_gtf) - h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - - (image_width * GTF_D_M_PRIME * 1000) + - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); - else - h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - - (image_width * GTF_S_M_PRIME * 1000) + - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); + if (default_gtf) { + u64 num; + u32 den; + + num = ((image_width * GTF_D_C_PRIME * (u64)hfreq) - + ((u64)image_width * GTF_D_M_PRIME * 1000)); + den = (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) * + (2 * GTF_CELL_GRAN); + h_blank = div_u64((num + (den >> 1)), den); + h_blank *= (2 * GTF_CELL_GRAN); + } else { + u64 num; + u32 den; + + num = ((image_width * GTF_S_C_PRIME * (u64)hfreq) - + ((u64)image_width * GTF_S_M_PRIME * 1000)); + den = (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) * + (2 * GTF_CELL_GRAN); + h_blank = div_u64((num + (den >> 1)), den); + h_blank *= (2 * GTF_CELL_GRAN); + } - h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); frame_width = image_width + h_blank; pix_clk = (image_width + h_blank) * hfreq; pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % GTF_CELL_GRAN; + hsync = ((hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN) * GTF_CELL_GRAN; h_fp = h_blank / 2 - hsync; @@ -571,11 +671,27 @@ bool v4l2_detect_gtf(unsigned frame_height, fmt->bt.hsync = hsync; fmt->bt.vsync = vsync; fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + + if (!interlaced) { + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.interlaced = V4L2_DV_PROGRESSIVE; + } else { + fmt->bt.vbackporch = (frame_height - image_height - 2 * v_fp - + 2 * vsync) / 2; + fmt->bt.il_vbackporch = frame_height - image_height - 2 * v_fp - + 2 * vsync - fmt->bt.vbackporch; + fmt->bt.il_vfrontporch = v_fp; + fmt->bt.il_vsync = vsync; + fmt->bt.flags |= V4L2_DV_FL_HALF_LINE; + fmt->bt.interlaced = V4L2_DV_INTERLACED; + } + fmt->bt.pixelclock = pix_clk; fmt->bt.standards = V4L2_DV_BT_STD_GTF; + if (!default_gtf) fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; } EXPORT_SYMBOL_GPL(v4l2_detect_gtf); @@ -592,7 +708,6 @@ EXPORT_SYMBOL_GPL(v4l2_detect_gtf); struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) { struct v4l2_fract aspect = { 16, 9 }; - u32 tmp; u8 ratio; /* Nothing filled in, fallback to 16:9 */ @@ -624,9 +739,7 @@ struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) if (hor_landscape) return aspect; /* The aspect ratio is for portrait, so swap numerator and denominator */ - tmp = aspect.denominator; - aspect.denominator = aspect.numerator; - aspect.numerator = tmp; + swap(aspect.denominator, aspect.numerator); return aspect; } EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); diff --git a/kernel/drivers/media/v4l2-core/v4l2-event.c b/kernel/drivers/media/v4l2-core/v4l2-event.c index 8761aab99..8d3171c6b 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-event.c +++ b/kernel/drivers/media/v4l2-core/v4l2-event.c @@ -172,6 +172,9 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) unsigned long flags; struct timespec timestamp; + if (vdev == NULL) + return; + ktime_get_ts(×tamp); spin_lock_irqsave(&vdev->fh_lock, flags); diff --git a/kernel/drivers/media/v4l2-core/v4l2-flash-led-class.c b/kernel/drivers/media/v4l2-core/v4l2-flash-led-class.c new file mode 100644 index 000000000..5bdfb8d52 --- /dev/null +++ b/kernel/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -0,0 +1,710 @@ +/* + * V4L2 flash LED sub-device registration helpers. + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd + * Author: Jacek Anaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define has_flash_op(v4l2_flash, op) \ + (v4l2_flash && v4l2_flash->ops->op) + +#define call_flash_op(v4l2_flash, op, arg) \ + (has_flash_op(v4l2_flash, op) ? \ + v4l2_flash->ops->op(v4l2_flash, arg) : \ + -EINVAL) + +enum ctrl_init_data_id { + LED_MODE, + TORCH_INTENSITY, + FLASH_INTENSITY, + INDICATOR_INTENSITY, + FLASH_TIMEOUT, + STROBE_SOURCE, + /* + * Only above values are applicable to + * the 'ctrls' array in the struct v4l2_flash. + */ + FLASH_STROBE, + STROBE_STOP, + STROBE_STATUS, + FLASH_FAULT, + NUM_FLASH_CTRLS, +}; + +static enum led_brightness __intensity_to_led_brightness( + struct v4l2_ctrl *ctrl, s32 intensity) +{ + intensity -= ctrl->minimum; + intensity /= (u32) ctrl->step; + + /* + * Indicator LEDs, unlike torch LEDs, are turned on/off basing on + * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. + * Therefore it must be possible to set it to 0 level which in + * the LED subsystem reflects LED_OFF state. + */ + if (ctrl->minimum) + ++intensity; + + return intensity; +} + +static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, + enum led_brightness brightness) +{ + /* + * Indicator LEDs, unlike torch LEDs, are turned on/off basing on + * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. + * Do not decrement brightness read from the LED subsystem for + * indicator LED as it may equal 0. For torch LEDs this function + * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the + * brightness read is guaranteed to be greater than 0. In the mode + * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used. + */ + if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY) + --brightness; + + return (brightness * ctrl->step) + ctrl->minimum; +} + +static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; + enum led_brightness brightness; + + if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) + brightness = call_flash_op(v4l2_flash, + intensity_to_led_brightness, + ctrl->val); + else + brightness = __intensity_to_led_brightness(ctrl, ctrl->val); + /* + * In case a LED Flash class driver provides ops for custom + * brightness <-> intensity conversion, it also must have defined + * related v4l2 control step == 1. In such a case a backward conversion + * from led brightness to v4l2 intensity is required to find out the + * the aligned intensity value. + */ + if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) + ctrl->val = call_flash_op(v4l2_flash, + led_brightness_to_intensity, + brightness); + + if (ctrl == ctrls[TORCH_INTENSITY]) { + if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) + return; + + led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, + brightness); + } else { + led_set_brightness(&v4l2_flash->iled_cdev->led_cdev, + brightness); + } +} + +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; + struct led_classdev *led_cdev; + int ret; + + if (ctrl == ctrls[TORCH_INTENSITY]) { + /* + * Update torch brightness only if in TORCH_MODE. In other modes + * torch led is turned off, which would spuriously inform the + * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value + * has changed to 0. + */ + if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) + return 0; + led_cdev = &v4l2_flash->fled_cdev->led_cdev; + } else { + led_cdev = &v4l2_flash->iled_cdev->led_cdev; + } + + ret = led_update_brightness(led_cdev); + if (ret < 0) + return ret; + + if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) + ctrl->val = call_flash_op(v4l2_flash, + led_brightness_to_intensity, + led_cdev->brightness); + else + ctrl->val = __led_brightness_to_intensity(ctrl, + led_cdev->brightness); + + return 0; +} + +static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) +{ + struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + bool is_strobing; + int ret; + + switch (c->id) { + case V4L2_CID_FLASH_TORCH_INTENSITY: + case V4L2_CID_FLASH_INDICATOR_INTENSITY: + return v4l2_flash_update_led_brightness(v4l2_flash, c); + case V4L2_CID_FLASH_INTENSITY: + ret = led_update_flash_brightness(fled_cdev); + if (ret < 0) + return ret; + /* + * No conversion is needed as LED Flash class also uses + * microamperes for flash intensity units. + */ + c->val = fled_cdev->brightness.val; + return 0; + case V4L2_CID_FLASH_STROBE_STATUS: + ret = led_get_flash_strobe(fled_cdev, &is_strobing); + if (ret < 0) + return ret; + c->val = is_strobing; + return 0; + case V4L2_CID_FLASH_FAULT: + /* LED faults map directly to V4L2 flash faults */ + return led_get_flash_fault(fled_cdev, &c->val); + default: + return -EINVAL; + } +} + +static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls) +{ + return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) || + (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val != + V4L2_FLASH_STROBE_SOURCE_SOFTWARE))); +} + +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) +{ + struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; + bool external_strobe; + int ret = 0; + + switch (c->id) { + case V4L2_CID_FLASH_LED_MODE: + switch (c->val) { + case V4L2_FLASH_LED_MODE_NONE: + led_set_brightness(led_cdev, LED_OFF); + return led_set_flash_strobe(fled_cdev, false); + case V4L2_FLASH_LED_MODE_FLASH: + /* Turn the torch LED off */ + led_set_brightness(led_cdev, LED_OFF); + if (ctrls[STROBE_SOURCE]) { + external_strobe = (ctrls[STROBE_SOURCE]->val == + V4L2_FLASH_STROBE_SOURCE_EXTERNAL); + + ret = call_flash_op(v4l2_flash, + external_strobe_set, + external_strobe); + } + return ret; + case V4L2_FLASH_LED_MODE_TORCH: + if (ctrls[STROBE_SOURCE]) { + ret = call_flash_op(v4l2_flash, + external_strobe_set, + false); + if (ret < 0) + return ret; + } + /* Stop flash strobing */ + ret = led_set_flash_strobe(fled_cdev, false); + if (ret < 0) + return ret; + + v4l2_flash_set_led_brightness(v4l2_flash, + ctrls[TORCH_INTENSITY]); + return 0; + } + break; + case V4L2_CID_FLASH_STROBE_SOURCE: + external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL); + /* + * For some hardware arrangements setting strobe source may + * affect torch mode. Therefore, if not in the flash mode, + * cache only this setting. It will be applied upon switching + * to flash mode. + */ + if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) + return 0; + + return call_flash_op(v4l2_flash, external_strobe_set, + external_strobe); + case V4L2_CID_FLASH_STROBE: + if (__software_strobe_mode_inactive(ctrls)) + return -EBUSY; + return led_set_flash_strobe(fled_cdev, true); + case V4L2_CID_FLASH_STROBE_STOP: + if (__software_strobe_mode_inactive(ctrls)) + return -EBUSY; + return led_set_flash_strobe(fled_cdev, false); + case V4L2_CID_FLASH_TIMEOUT: + /* + * No conversion is needed as LED Flash class also uses + * microseconds for flash timeout units. + */ + return led_set_flash_timeout(fled_cdev, c->val); + case V4L2_CID_FLASH_INTENSITY: + /* + * No conversion is needed as LED Flash class also uses + * microamperes for flash intensity units. + */ + return led_set_flash_brightness(fled_cdev, c->val); + case V4L2_CID_FLASH_TORCH_INTENSITY: + case V4L2_CID_FLASH_INDICATOR_INTENSITY: + v4l2_flash_set_led_brightness(v4l2_flash, c); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = { + .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl, + .s_ctrl = v4l2_flash_s_ctrl, +}; + +static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s, + struct v4l2_ctrl_config *c) +{ + c->min = s->min; + c->max = s->max; + c->step = s->step; + c->def = s->val; +} + +static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash, + struct v4l2_flash_config *flash_cfg, + struct v4l2_flash_ctrl_data *ctrl_init_data) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct v4l2_ctrl_config *ctrl_cfg; + u32 mask; + + /* Init FLASH_FAULT ctrl data */ + if (flash_cfg->flash_faults) { + ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT; + ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config; + ctrl_cfg->id = V4L2_CID_FLASH_FAULT; + ctrl_cfg->max = flash_cfg->flash_faults; + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; + } + + /* Init FLASH_LED_MODE ctrl data */ + mask = 1 << V4L2_FLASH_LED_MODE_NONE | + 1 << V4L2_FLASH_LED_MODE_TORCH; + if (led_cdev->flags & LED_DEV_CAP_FLASH) + mask |= 1 << V4L2_FLASH_LED_MODE_FLASH; + + ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE; + ctrl_cfg = &ctrl_init_data[LED_MODE].config; + ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE; + ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH; + ctrl_cfg->menu_skip_mask = ~mask; + ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE; + ctrl_cfg->flags = 0; + + /* Init TORCH_INTENSITY ctrl data */ + ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY; + ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config; + __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg); + ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY; + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + + /* Init INDICATOR_INTENSITY ctrl data */ + if (v4l2_flash->iled_cdev) { + ctrl_init_data[INDICATOR_INTENSITY].cid = + V4L2_CID_FLASH_INDICATOR_INTENSITY; + ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config; + __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity, + ctrl_cfg); + ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY; + ctrl_cfg->min = 0; + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + } + + if (!(led_cdev->flags & LED_DEV_CAP_FLASH)) + return; + + /* Init FLASH_STROBE ctrl data */ + ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE; + ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config; + ctrl_cfg->id = V4L2_CID_FLASH_STROBE; + + /* Init STROBE_STOP ctrl data */ + ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP; + ctrl_cfg = &ctrl_init_data[STROBE_STOP].config; + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP; + + /* Init FLASH_STROBE_SOURCE ctrl data */ + if (flash_cfg->has_external_strobe) { + mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) | + (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL); + ctrl_init_data[STROBE_SOURCE].cid = + V4L2_CID_FLASH_STROBE_SOURCE; + ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config; + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE; + ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL; + ctrl_cfg->menu_skip_mask = ~mask; + ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; + } + + /* Init STROBE_STATUS ctrl data */ + if (fled_cdev_ops->strobe_get) { + ctrl_init_data[STROBE_STATUS].cid = + V4L2_CID_FLASH_STROBE_STATUS; + ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config; + ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS; + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; + } + + /* Init FLASH_TIMEOUT ctrl data */ + if (fled_cdev_ops->timeout_set) { + ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT; + ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config; + __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg); + ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT; + } + + /* Init FLASH_INTENSITY ctrl data */ + if (fled_cdev_ops->flash_brightness_set) { + ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY; + ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config; + __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg); + ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY; + ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + } +} + +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, + struct v4l2_flash_config *flash_cfg) + +{ + struct v4l2_flash_ctrl_data *ctrl_init_data; + struct v4l2_ctrl *ctrl; + struct v4l2_ctrl_config *ctrl_cfg; + int i, ret, num_ctrls = 0; + + v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev, + sizeof(*v4l2_flash->ctrls) * + (STROBE_SOURCE + 1), GFP_KERNEL); + if (!v4l2_flash->ctrls) + return -ENOMEM; + + /* allocate memory dynamically so as not to exceed stack frame size */ + ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data), + GFP_KERNEL); + if (!ctrl_init_data) + return -ENOMEM; + + __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data); + + for (i = 0; i < NUM_FLASH_CTRLS; ++i) + if (ctrl_init_data[i].cid) + ++num_ctrls; + + v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls); + + for (i = 0; i < NUM_FLASH_CTRLS; ++i) { + ctrl_cfg = &ctrl_init_data[i].config; + if (!ctrl_init_data[i].cid) + continue; + + if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE || + ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE) + ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl, + &v4l2_flash_ctrl_ops, + ctrl_cfg->id, + ctrl_cfg->max, + ctrl_cfg->menu_skip_mask, + ctrl_cfg->def); + else + ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, + &v4l2_flash_ctrl_ops, + ctrl_cfg->id, + ctrl_cfg->min, + ctrl_cfg->max, + ctrl_cfg->step, + ctrl_cfg->def); + + if (ctrl) + ctrl->flags |= ctrl_cfg->flags; + + if (i <= STROBE_SOURCE) + v4l2_flash->ctrls[i] = ctrl; + } + + kfree(ctrl_init_data); + + if (v4l2_flash->hdl.error) { + ret = v4l2_flash->hdl.error; + goto error_free_handler; + } + + v4l2_ctrl_handler_setup(&v4l2_flash->hdl); + + v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl; + + return 0; + +error_free_handler: + v4l2_ctrl_handler_free(&v4l2_flash->hdl); + return ret; +} + +static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; + int ret = 0; + + v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]); + + if (ctrls[INDICATOR_INTENSITY]) + v4l2_flash_set_led_brightness(v4l2_flash, + ctrls[INDICATOR_INTENSITY]); + + if (ctrls[FLASH_TIMEOUT]) { + ret = led_set_flash_timeout(fled_cdev, + ctrls[FLASH_TIMEOUT]->val); + if (ret < 0) + return ret; + } + + if (ctrls[FLASH_INTENSITY]) { + ret = led_set_flash_brightness(fled_cdev, + ctrls[FLASH_INTENSITY]->val); + if (ret < 0) + return ret; + } + + /* + * For some hardware arrangements setting strobe source may affect + * torch mode. Synchronize strobe source setting only if not in torch + * mode. For torch mode case it will get synchronized upon switching + * to flash mode. + */ + if (ctrls[STROBE_SOURCE] && + ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) + ret = call_flash_op(v4l2_flash, external_strobe_set, + ctrls[STROBE_SOURCE]->val); + + return ret; +} + +/* + * V4L2 subdev internal operations + */ + +static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; + struct led_classdev *led_cdev_ind = NULL; + int ret = 0; + + if (!v4l2_fh_is_singular(&fh->vfh)) + return 0; + + mutex_lock(&led_cdev->led_access); + + led_sysfs_disable(led_cdev); + led_trigger_remove(led_cdev); + + mutex_unlock(&led_cdev->led_access); + + if (iled_cdev) { + led_cdev_ind = &iled_cdev->led_cdev; + + mutex_lock(&led_cdev_ind->led_access); + + led_sysfs_disable(led_cdev_ind); + led_trigger_remove(led_cdev_ind); + + mutex_unlock(&led_cdev_ind->led_access); + } + + ret = __sync_device_with_v4l2_controls(v4l2_flash); + if (ret < 0) + goto out_sync_device; + + return 0; +out_sync_device: + mutex_lock(&led_cdev->led_access); + led_sysfs_enable(led_cdev); + mutex_unlock(&led_cdev->led_access); + + if (led_cdev_ind) { + mutex_lock(&led_cdev_ind->led_access); + led_sysfs_enable(led_cdev_ind); + mutex_unlock(&led_cdev_ind->led_access); + } + + return ret; +} + +static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; + int ret = 0; + + if (!v4l2_fh_is_singular(&fh->vfh)) + return 0; + + mutex_lock(&led_cdev->led_access); + + if (v4l2_flash->ctrls[STROBE_SOURCE]) + ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE], + V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + led_sysfs_enable(led_cdev); + + mutex_unlock(&led_cdev->led_access); + + if (iled_cdev) { + struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev; + + mutex_lock(&led_cdev_ind->led_access); + led_sysfs_enable(led_cdev_ind); + mutex_unlock(&led_cdev_ind->led_access); + } + + return ret; +} + +static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { + .open = v4l2_flash_open, + .close = v4l2_flash_close, +}; + +static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = { + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = { + .core = &v4l2_flash_core_ops, +}; + +struct v4l2_flash *v4l2_flash_init( + struct device *dev, struct device_node *of_node, + struct led_classdev_flash *fled_cdev, + struct led_classdev_flash *iled_cdev, + const struct v4l2_flash_ops *ops, + struct v4l2_flash_config *config) +{ + struct v4l2_flash *v4l2_flash; + struct led_classdev *led_cdev; + struct v4l2_subdev *sd; + int ret; + + if (!fled_cdev || !ops || !config) + return ERR_PTR(-EINVAL); + + led_cdev = &fled_cdev->led_cdev; + + v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash), + GFP_KERNEL); + if (!v4l2_flash) + return ERR_PTR(-ENOMEM); + + sd = &v4l2_flash->sd; + v4l2_flash->fled_cdev = fled_cdev; + v4l2_flash->iled_cdev = iled_cdev; + v4l2_flash->ops = ops; + sd->dev = dev; + sd->of_node = of_node; + v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); + sd->internal_ops = &v4l2_flash_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + strlcpy(sd->name, config->dev_name, sizeof(sd->name)); + + ret = media_entity_init(&sd->entity, 0, NULL, 0); + if (ret < 0) + return ERR_PTR(ret); + + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + ret = v4l2_flash_init_controls(v4l2_flash, config); + if (ret < 0) + goto err_init_controls; + + if (sd->of_node) + of_node_get(sd->of_node); + else + of_node_get(led_cdev->dev->of_node); + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto err_async_register_sd; + + return v4l2_flash; + +err_async_register_sd: + of_node_put(led_cdev->dev->of_node); + v4l2_ctrl_handler_free(sd->ctrl_handler); +err_init_controls: + media_entity_cleanup(&sd->entity); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(v4l2_flash_init); + +void v4l2_flash_release(struct v4l2_flash *v4l2_flash) +{ + struct v4l2_subdev *sd; + struct led_classdev *led_cdev; + + if (IS_ERR_OR_NULL(v4l2_flash)) + return; + + sd = &v4l2_flash->sd; + led_cdev = &v4l2_flash->fled_cdev->led_cdev; + + v4l2_async_unregister_subdev(sd); + + if (sd->of_node) + of_node_put(sd->of_node); + else + of_node_put(led_cdev->dev->of_node); + + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); +} +EXPORT_SYMBOL_GPL(v4l2_flash_release); + +MODULE_AUTHOR("Jacek Anaszewski "); +MODULE_DESCRIPTION("V4L2 Flash sub-device helpers"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/media/v4l2-core/v4l2-ioctl.c b/kernel/drivers/media/v4l2-core/v4l2-ioctl.c index aa407cb5f..7486af2c8 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/kernel/drivers/media/v4l2-core/v4l2-ioctl.c @@ -26,9 +26,8 @@ #include #include #include -#include +#include -#define CREATE_TRACE_POINTS #include /* Zero out the end of the struct pointed to by p. Everything after, but @@ -142,6 +141,7 @@ const char *v4l2_field_names[] = { EXPORT_SYMBOL(v4l2_field_names); const char *v4l2_type_names[] = { + [0] = "0", [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap", [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay", [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out", @@ -153,6 +153,7 @@ const char *v4l2_type_names[] = { [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane", [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane", [V4L2_BUF_TYPE_SDR_CAPTURE] = "sdr-cap", + [V4L2_BUF_TYPE_SDR_OUTPUT] = "sdr-out", }; EXPORT_SYMBOL(v4l2_type_names); @@ -257,7 +258,8 @@ static void v4l_print_format(const void *arg, bool write_only) pr_cont(", width=%u, height=%u, " "pixelformat=%c%c%c%c, field=%s, " "bytesperline=%u, sizeimage=%u, colorspace=%d, " - "flags=0x%x, ycbcr_enc=%u, quantization=%u\n", + "flags=0x%x, ycbcr_enc=%u, quantization=%u, " + "xfer_func=%u\n", pix->width, pix->height, (pix->pixelformat & 0xff), (pix->pixelformat >> 8) & 0xff, @@ -266,7 +268,7 @@ static void v4l_print_format(const void *arg, bool write_only) prt_names(pix->field, v4l2_field_names), pix->bytesperline, pix->sizeimage, pix->colorspace, pix->flags, pix->ycbcr_enc, - pix->quantization); + pix->quantization, pix->xfer_func); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -274,7 +276,7 @@ static void v4l_print_format(const void *arg, bool write_only) pr_cont(", width=%u, height=%u, " "format=%c%c%c%c, field=%s, " "colorspace=%d, num_planes=%u, flags=0x%x, " - "ycbcr_enc=%u, quantization=%u\n", + "ycbcr_enc=%u, quantization=%u, xfer_func=%u\n", mp->width, mp->height, (mp->pixelformat & 0xff), (mp->pixelformat >> 8) & 0xff, @@ -282,7 +284,7 @@ static void v4l_print_format(const void *arg, bool write_only) (mp->pixelformat >> 24) & 0xff, prt_names(mp->field, v4l2_field_names), mp->colorspace, mp->num_planes, mp->flags, - mp->ycbcr_enc, mp->quantization); + mp->ycbcr_enc, mp->quantization, mp->xfer_func); for (i = 0; i < mp->num_planes; i++) printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i, mp->plane_fmt[i].bytesperline, @@ -325,6 +327,7 @@ static void v4l_print_format(const void *arg, bool write_only) sliced->service_lines[1][i]); break; case V4L2_BUF_TYPE_SDR_CAPTURE: + case V4L2_BUF_TYPE_SDR_OUTPUT: sdr = &p->fmt.sdr; pr_cont(", pixelformat=%c%c%c%c\n", (sdr->pixelformat >> 0) & 0xff, @@ -973,6 +976,10 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) if (is_sdr && is_rx && ops->vidioc_g_fmt_sdr_cap) return 0; break; + case V4L2_BUF_TYPE_SDR_OUTPUT: + if (is_sdr && is_tx && ops->vidioc_g_fmt_sdr_out) + return 0; + break; default: break; } @@ -1023,8 +1030,9 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, * Drivers MUST fill in device_caps, so check for this and * warn if it was forgotten. */ - WARN_ON(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) || - !cap->device_caps); + WARN(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) || + !cap->device_caps, "Bad caps for driver %s, %x %x", + cap->driver, cap->capabilities, cap->device_caps); cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return ret; @@ -1103,6 +1111,183 @@ static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, return ops->vidioc_enum_output(file, fh, p); } +static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) +{ + const unsigned sz = sizeof(fmt->description); + const char *descr = NULL; + u32 flags = 0; + + /* + * We depart from the normal coding style here since the descriptions + * should be aligned so it is easy to see which descriptions will be + * longer than 31 characters (the max length for a description). + * And frankly, this is easier to read anyway. + * + * Note that gcc will use O(log N) comparisons to find the right case. + */ + switch (fmt->pixelformat) { + /* Max description length mask: descr = "0123456789012345678901234567890" */ + case V4L2_PIX_FMT_RGB332: descr = "8-bit RGB 3-3-2"; break; + case V4L2_PIX_FMT_RGB444: descr = "16-bit A/XRGB 4-4-4-4"; break; + case V4L2_PIX_FMT_ARGB444: descr = "16-bit ARGB 4-4-4-4"; break; + case V4L2_PIX_FMT_XRGB444: descr = "16-bit XRGB 4-4-4-4"; break; + case V4L2_PIX_FMT_RGB555: descr = "16-bit A/XRGB 1-5-5-5"; break; + case V4L2_PIX_FMT_ARGB555: descr = "16-bit ARGB 1-5-5-5"; break; + case V4L2_PIX_FMT_XRGB555: descr = "16-bit XRGB 1-5-5-5"; break; + case V4L2_PIX_FMT_RGB565: descr = "16-bit RGB 5-6-5"; break; + case V4L2_PIX_FMT_RGB555X: descr = "16-bit A/XRGB 1-5-5-5 BE"; break; + case V4L2_PIX_FMT_ARGB555X: descr = "16-bit ARGB 1-5-5-5 BE"; break; + case V4L2_PIX_FMT_XRGB555X: descr = "16-bit XRGB 1-5-5-5 BE"; break; + case V4L2_PIX_FMT_RGB565X: descr = "16-bit RGB 5-6-5 BE"; break; + case V4L2_PIX_FMT_BGR666: descr = "18-bit BGRX 6-6-6-14"; break; + case V4L2_PIX_FMT_BGR24: descr = "24-bit BGR 8-8-8"; break; + case V4L2_PIX_FMT_RGB24: descr = "24-bit RGB 8-8-8"; break; + case V4L2_PIX_FMT_BGR32: descr = "32-bit BGRA/X 8-8-8-8"; break; + case V4L2_PIX_FMT_ABGR32: descr = "32-bit BGRA 8-8-8-8"; break; + case V4L2_PIX_FMT_XBGR32: descr = "32-bit BGRX 8-8-8-8"; break; + case V4L2_PIX_FMT_RGB32: descr = "32-bit A/XRGB 8-8-8-8"; break; + case V4L2_PIX_FMT_ARGB32: descr = "32-bit ARGB 8-8-8-8"; break; + case V4L2_PIX_FMT_XRGB32: descr = "32-bit XRGB 8-8-8-8"; break; + case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break; + case V4L2_PIX_FMT_Y4: descr = "4-bit Greyscale"; break; + case V4L2_PIX_FMT_Y6: descr = "6-bit Greyscale"; break; + case V4L2_PIX_FMT_Y10: descr = "10-bit Greyscale"; break; + case V4L2_PIX_FMT_Y12: descr = "12-bit Greyscale"; break; + case V4L2_PIX_FMT_Y16: descr = "16-bit Greyscale"; break; + case V4L2_PIX_FMT_Y16_BE: descr = "16-bit Greyscale BE"; break; + case V4L2_PIX_FMT_Y10BPACK: descr = "10-bit Greyscale (Packed)"; break; + case V4L2_PIX_FMT_PAL8: descr = "8-bit Palette"; break; + case V4L2_PIX_FMT_UV8: descr = "8-bit Chrominance UV 4-4"; break; + case V4L2_PIX_FMT_YVU410: descr = "Planar YVU 4:1:0"; break; + case V4L2_PIX_FMT_YVU420: descr = "Planar YVU 4:2:0"; break; + case V4L2_PIX_FMT_YUYV: descr = "YUYV 4:2:2"; break; + case V4L2_PIX_FMT_YYUV: descr = "YYUV 4:2:2"; break; + case V4L2_PIX_FMT_YVYU: descr = "YVYU 4:2:2"; break; + case V4L2_PIX_FMT_UYVY: descr = "UYVY 4:2:2"; break; + case V4L2_PIX_FMT_VYUY: descr = "VYUY 4:2:2"; break; + case V4L2_PIX_FMT_YUV422P: descr = "Planar YVU 4:2:2"; break; + case V4L2_PIX_FMT_YUV411P: descr = "Planar YUV 4:1:1"; break; + case V4L2_PIX_FMT_Y41P: descr = "YUV 4:1:1 (Packed)"; break; + case V4L2_PIX_FMT_YUV444: descr = "16-bit A/XYUV 4-4-4-4"; break; + case V4L2_PIX_FMT_YUV555: descr = "16-bit A/XYUV 1-5-5-5"; break; + case V4L2_PIX_FMT_YUV565: descr = "16-bit YUV 5-6-5"; break; + case V4L2_PIX_FMT_YUV32: descr = "32-bit A/XYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break; + case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; + case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; + case V4L2_PIX_FMT_HM12: descr = "YUV 4:2:0 (16x16 Macroblocks)"; break; + case V4L2_PIX_FMT_M420: descr = "YUV 4:2:0 (M420)"; break; + case V4L2_PIX_FMT_NV12: descr = "Y/CbCr 4:2:0"; break; + case V4L2_PIX_FMT_NV21: descr = "Y/CrCb 4:2:0"; break; + case V4L2_PIX_FMT_NV16: descr = "Y/CbCr 4:2:2"; break; + case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break; + case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break; + case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break; + case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break; + case V4L2_PIX_FMT_NV61M: descr = "Y/CrCb 4:2:2 (N-C)"; break; + case V4L2_PIX_FMT_NV12MT: descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break; + case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break; + case V4L2_PIX_FMT_YUV420M: descr = "Planar YUV 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_YVU420M: descr = "Planar YVU 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_SBGGR8: descr = "8-bit Bayer BGBG/GRGR"; break; + case V4L2_PIX_FMT_SGBRG8: descr = "8-bit Bayer GBGB/RGRG"; break; + case V4L2_PIX_FMT_SGRBG8: descr = "8-bit Bayer GRGR/BGBG"; break; + case V4L2_PIX_FMT_SRGGB8: descr = "8-bit Bayer RGRG/GBGB"; break; + case V4L2_PIX_FMT_SBGGR10: descr = "10-bit Bayer BGBG/GRGR"; break; + case V4L2_PIX_FMT_SGBRG10: descr = "10-bit Bayer GBGB/RGRG"; break; + case V4L2_PIX_FMT_SGRBG10: descr = "10-bit Bayer GRGR/BGBG"; break; + case V4L2_PIX_FMT_SRGGB10: descr = "10-bit Bayer RGRG/GBGB"; break; + case V4L2_PIX_FMT_SBGGR12: descr = "12-bit Bayer BGBG/GRGR"; break; + case V4L2_PIX_FMT_SGBRG12: descr = "12-bit Bayer GBGB/RGRG"; break; + case V4L2_PIX_FMT_SGRBG12: descr = "12-bit Bayer GRGR/BGBG"; break; + case V4L2_PIX_FMT_SRGGB12: descr = "12-bit Bayer RGRG/GBGB"; break; + case V4L2_PIX_FMT_SBGGR10P: descr = "10-bit Bayer BGBG/GRGR Packed"; break; + case V4L2_PIX_FMT_SGBRG10P: descr = "10-bit Bayer GBGB/RGRG Packed"; break; + case V4L2_PIX_FMT_SGRBG10P: descr = "10-bit Bayer GRGR/BGBG Packed"; break; + case V4L2_PIX_FMT_SRGGB10P: descr = "10-bit Bayer RGRG/GBGB Packed"; break; + case V4L2_PIX_FMT_SBGGR10ALAW8: descr = "8-bit Bayer BGBG/GRGR (A-law)"; break; + case V4L2_PIX_FMT_SGBRG10ALAW8: descr = "8-bit Bayer GBGB/RGRG (A-law)"; break; + case V4L2_PIX_FMT_SGRBG10ALAW8: descr = "8-bit Bayer GRGR/BGBG (A-law)"; break; + case V4L2_PIX_FMT_SRGGB10ALAW8: descr = "8-bit Bayer RGRG/GBGB (A-law)"; break; + case V4L2_PIX_FMT_SBGGR10DPCM8: descr = "8-bit Bayer BGBG/GRGR (DPCM)"; break; + case V4L2_PIX_FMT_SGBRG10DPCM8: descr = "8-bit Bayer GBGB/RGRG (DPCM)"; break; + case V4L2_PIX_FMT_SGRBG10DPCM8: descr = "8-bit Bayer GRGR/BGBG (DPCM)"; break; + case V4L2_PIX_FMT_SRGGB10DPCM8: descr = "8-bit Bayer RGRG/GBGB (DPCM)"; break; + case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR (Exp.)"; break; + case V4L2_PIX_FMT_SN9C20X_I420: descr = "GSPCA SN9C20X I420"; break; + case V4L2_PIX_FMT_SPCA501: descr = "GSPCA SPCA501"; break; + case V4L2_PIX_FMT_SPCA505: descr = "GSPCA SPCA505"; break; + case V4L2_PIX_FMT_SPCA508: descr = "GSPCA SPCA508"; break; + case V4L2_PIX_FMT_STV0680: descr = "GSPCA STV0680"; break; + case V4L2_PIX_FMT_TM6000: descr = "A/V + VBI Mux Packet"; break; + case V4L2_PIX_FMT_CIT_YYVYUY: descr = "GSPCA CIT YYVYUY"; break; + case V4L2_PIX_FMT_KONICA420: descr = "GSPCA KONICA420"; break; + case V4L2_SDR_FMT_CU8: descr = "Complex U8"; break; + case V4L2_SDR_FMT_CU16LE: descr = "Complex U16LE"; break; + 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; + + default: + /* Compressed formats */ + flags = V4L2_FMT_FLAG_COMPRESSED; + switch (fmt->pixelformat) { + /* Max description length mask: descr = "0123456789012345678901234567890" */ + case V4L2_PIX_FMT_MJPEG: descr = "Motion-JPEG"; break; + case V4L2_PIX_FMT_JPEG: descr = "JFIF JPEG"; break; + case V4L2_PIX_FMT_DV: descr = "1394"; break; + case V4L2_PIX_FMT_MPEG: descr = "MPEG-1/2/4"; break; + case V4L2_PIX_FMT_H264: descr = "H.264"; break; + case V4L2_PIX_FMT_H264_NO_SC: descr = "H.264 (No Start Codes)"; break; + case V4L2_PIX_FMT_H264_MVC: descr = "H.264 MVC"; break; + case V4L2_PIX_FMT_H263: descr = "H.263"; break; + case V4L2_PIX_FMT_MPEG1: descr = "MPEG-1 ES"; break; + case V4L2_PIX_FMT_MPEG2: descr = "MPEG-2 ES"; break; + case V4L2_PIX_FMT_MPEG4: descr = "MPEG-4 part 2 ES"; break; + case V4L2_PIX_FMT_XVID: descr = "Xvid"; break; + case V4L2_PIX_FMT_VC1_ANNEX_G: descr = "VC-1 (SMPTE 412M Annex G)"; break; + case V4L2_PIX_FMT_VC1_ANNEX_L: descr = "VC-1 (SMPTE 412M Annex L)"; break; + case V4L2_PIX_FMT_VP8: descr = "VP8"; break; + case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; + case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break; + case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break; + case V4L2_PIX_FMT_PWC1: descr = "Raw Philips Webcam Type (Old)"; break; + case V4L2_PIX_FMT_PWC2: descr = "Raw Philips Webcam Type (New)"; break; + case V4L2_PIX_FMT_ET61X251: descr = "GSPCA ET61X251"; break; + case V4L2_PIX_FMT_SPCA561: descr = "GSPCA SPCA561"; break; + case V4L2_PIX_FMT_PAC207: descr = "GSPCA PAC207"; break; + case V4L2_PIX_FMT_MR97310A: descr = "GSPCA MR97310A"; break; + case V4L2_PIX_FMT_JL2005BCD: descr = "GSPCA JL2005BCD"; break; + case V4L2_PIX_FMT_SN9C2028: descr = "GSPCA SN9C2028"; break; + case V4L2_PIX_FMT_SQ905C: descr = "GSPCA SQ905C"; break; + case V4L2_PIX_FMT_PJPG: descr = "GSPCA PJPG"; break; + case V4L2_PIX_FMT_OV511: descr = "GSPCA OV511"; break; + case V4L2_PIX_FMT_OV518: descr = "GSPCA OV518"; break; + case V4L2_PIX_FMT_JPGL: descr = "JPEG Lite"; break; + case V4L2_PIX_FMT_SE401: descr = "GSPCA SE401"; break; + case V4L2_PIX_FMT_S5C_UYVY_JPG: descr = "S5C73MX interleaved UYVY/JPEG"; break; + default: + WARN(1, "Unknown pixelformat 0x%08x\n", fmt->pixelformat); + if (fmt->description[0]) + return; + flags = 0; + snprintf(fmt->description, sz, "%c%c%c%c%s", + (char)(fmt->pixelformat & 0x7f), + (char)((fmt->pixelformat >> 8) & 0x7f), + (char)((fmt->pixelformat >> 16) & 0x7f), + (char)((fmt->pixelformat >> 24) & 0x7f), + (fmt->pixelformat & (1 << 31)) ? "-BE" : ""); + break; + } + } + + if (descr) + WARN_ON(strlcpy(fmt->description, descr, sz) >= sz); + fmt->flags = flags; +} + static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1112,34 +1297,48 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret = -EINVAL; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap)) break; - return ops->vidioc_enum_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg); + break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane)) break; - return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); + ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay)) break; - return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); + ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out)) break; - return ops->vidioc_enum_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane)) break; - return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); + ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); + break; case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap)) break; - return ops->vidioc_enum_fmt_sdr_cap(file, fh, arg); + ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg); + break; + case V4L2_BUF_TYPE_SDR_OUTPUT: + if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out)) + break; + ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg); + break; } - return -EINVAL; + if (ret == 0) + v4l_fill_fmtdesc(p); + return ret; } static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, @@ -1230,6 +1429,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap)) break; return ops->vidioc_g_fmt_sdr_cap(file, fh, arg); + case V4L2_BUF_TYPE_SDR_OUTPUT: + if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out)) + break; + return ops->vidioc_g_fmt_sdr_out(file, fh, arg); } return -EINVAL; } @@ -1309,6 +1512,11 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, break; CLEAR_AFTER_FIELD(p, fmt.sdr); return ops->vidioc_s_fmt_sdr_cap(file, fh, arg); + case V4L2_BUF_TYPE_SDR_OUTPUT: + if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out)) + break; + CLEAR_AFTER_FIELD(p, fmt.sdr); + return ops->vidioc_s_fmt_sdr_out(file, fh, arg); } return -EINVAL; } @@ -1388,6 +1596,11 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, break; CLEAR_AFTER_FIELD(p, fmt.sdr); return ops->vidioc_try_fmt_sdr_cap(file, fh, arg); + case V4L2_BUF_TYPE_SDR_OUTPUT: + if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out)) + break; + CLEAR_AFTER_FIELD(p, fmt.sdr); + return ops->vidioc_try_fmt_sdr_out(file, fh, arg); } return -EINVAL; } @@ -1433,15 +1646,31 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vfd = video_devdata(file); struct v4l2_modulator *p = arg; int err; + if (vfd->vfl_type == VFL_TYPE_RADIO) + p->type = V4L2_TUNER_RADIO; + err = ops->vidioc_g_modulator(file, fh, p); if (!err) p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; return err; } +static int v4l_s_modulator(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_modulator *p = arg; + + if (vfd->vfl_type == VFL_TYPE_RADIO) + p->type = V4L2_TUNER_RADIO; + + return ops->vidioc_s_modulator(file, fh, p); +} + static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1449,7 +1678,7 @@ static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, struct v4l2_frequency *p = arg; if (vfd->vfl_type == VFL_TYPE_SDR) - p->type = V4L2_TUNER_ADC; + p->type = V4L2_TUNER_SDR; else p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; @@ -1464,7 +1693,7 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops, enum v4l2_tuner_type type; if (vfd->vfl_type == VFL_TYPE_SDR) { - if (p->type != V4L2_TUNER_ADC && p->type != V4L2_TUNER_RF) + if (p->type != V4L2_TUNER_SDR && p->type != V4L2_TUNER_RF) return -EINVAL; } else { type = (vfd->vfl_type == VFL_TYPE_RADIO) ? @@ -1618,6 +1847,8 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; + CLEAR_AFTER_FIELD(create, format); + v4l_sanitize_format(&create->format); ret = ops->vidioc_create_bufs(file, fh, create); @@ -2087,7 +2318,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, int err; if (vfd->vfl_type == VFL_TYPE_SDR) { - if (p->type != V4L2_TUNER_ADC && p->type != V4L2_TUNER_RF) + if (p->type != V4L2_TUNER_SDR && p->type != V4L2_TUNER_RF) return -EINVAL; type = p->type; } else { @@ -2226,7 +2457,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), - IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), @@ -2354,7 +2585,7 @@ static long __video_do_ioctl(struct file *file, if (v4l2_is_known_ioctl(cmd)) { info = &v4l2_ioctls[_IOC_NR(cmd)]; - if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && + if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) goto done; diff --git a/kernel/drivers/media/v4l2-core/v4l2-mem2mem.c b/kernel/drivers/media/v4l2-core/v4l2-mem2mem.c index 73824a5ad..61d56c940 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/kernel/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include @@ -357,9 +357,16 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { struct vb2_queue *vq; + int ret; vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); - return vb2_reqbufs(vq, reqbufs); + ret = vb2_reqbufs(vq, reqbufs); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have an owner. */ + if (ret == 0) + vq->owner = reqbufs->count ? file->private_data : NULL; + + return ret; } EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); @@ -426,6 +433,25 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); +/** + * v4l2_m2m_prepare_buf() - prepare a source or destination buffer, depending on + * the type + */ +int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = vb2_prepare_buf(vq, buf); + if (!ret) + v4l2_m2m_try_schedule(m2m_ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf); + /** * v4l2_m2m_create_bufs() - create a source or destination buffer, depending * on the type @@ -557,24 +583,25 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, goto end; } - if (m2m_ctx->m2m_dev->m2m_ops->unlock) - m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv); - else if (m2m_ctx->q_lock) - mutex_unlock(m2m_ctx->q_lock); - + spin_lock_irqsave(&src_q->done_lock, flags); if (list_empty(&src_q->done_list)) poll_wait(file, &src_q->done_wq, wait); - if (list_empty(&dst_q->done_list)) - poll_wait(file, &dst_q->done_wq, wait); + spin_unlock_irqrestore(&src_q->done_lock, flags); - if (m2m_ctx->m2m_dev->m2m_ops->lock) - m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); - else if (m2m_ctx->q_lock) { - if (mutex_lock_interruptible(m2m_ctx->q_lock)) { - rc |= POLLERR; - goto end; + spin_lock_irqsave(&dst_q->done_lock, flags); + if (list_empty(&dst_q->done_list)) { + /* + * If the last buffer was dequeued from the capture queue, + * return immediately. DQBUF will return -EPIPE. + */ + if (dst_q->last_buffer_dequeued) { + spin_unlock_irqrestore(&dst_q->done_lock, flags); + return rc | POLLIN | POLLRDNORM; } + + poll_wait(file, &dst_q->done_wq, wait); } + spin_unlock_irqrestore(&dst_q->done_lock, flags); spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) @@ -739,13 +766,15 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); * * Call from buf_queue(), videobuf_queue_ops callback. */ -void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb) +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) { - struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb); + struct v4l2_m2m_buffer *b = container_of(vbuf, + struct v4l2_m2m_buffer, vb); struct v4l2_m2m_queue_ctx *q_ctx; unsigned long flags; - q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type); + q_ctx = get_queue_ctx(m2m_ctx, vbuf->vb2_buf.vb2_queue->type); if (!q_ctx) return; @@ -803,6 +832,15 @@ int v4l2_m2m_ioctl_dqbuf(struct file *file, void *priv, } EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_dqbuf); +int v4l2_m2m_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_prepare_buf(file, fh->m2m_ctx, buf); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_prepare_buf); + int v4l2_m2m_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *eb) { @@ -838,18 +876,8 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff); int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct v4l2_fh *fh = file->private_data; - struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx; - int ret; - - if (m2m_ctx->q_lock && mutex_lock_interruptible(m2m_ctx->q_lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_mmap(file, m2m_ctx, vma); - if (m2m_ctx->q_lock) - mutex_unlock(m2m_ctx->q_lock); - - return ret; + return v4l2_m2m_mmap(file, fh->m2m_ctx, vma); } EXPORT_SYMBOL_GPL(v4l2_m2m_fop_mmap); diff --git a/kernel/drivers/media/v4l2-core/v4l2-of.c b/kernel/drivers/media/v4l2-core/v4l2-of.c index 83143d39d..b27cbb1f5 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-of.c +++ b/kernel/drivers/media/v4l2-core/v4l2-of.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -92,10 +93,6 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : V4L2_MBUS_VSYNC_ACTIVE_LOW; - 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, "field-even-active", &v)) flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : V4L2_MBUS_FIELD_EVEN_LOW; @@ -104,6 +101,10 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, 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; @@ -141,6 +142,10 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, * 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. */ int v4l2_of_parse_endpoint(const struct device_node *node, @@ -149,8 +154,9 @@ int v4l2_of_parse_endpoint(const struct device_node *node, int rval; of_graph_parse_endpoint(node, &endpoint->base); - endpoint->bus_type = 0; - memset(&endpoint->bus, 0, sizeof(endpoint->bus)); + /* 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) @@ -166,6 +172,88 @@ int v4l2_of_parse_endpoint(const struct device_node *node, } 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 diff --git a/kernel/drivers/media/v4l2-core/v4l2-subdev.c b/kernel/drivers/media/v4l2-core/v4l2-subdev.c index 63596063b..83615b8fb 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-subdev.c +++ b/kernel/drivers/media/v4l2-core/v4l2-subdev.c @@ -588,3 +588,21 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) #endif } EXPORT_SYMBOL(v4l2_subdev_init); + +/** + * v4l2_subdev_notify_event() - Delivers event notification for subdevice + * @sd: The subdev for which to deliver the event + * @ev: The event to deliver + * + * Will deliver the specified event to all userspace event listeners which are + * subscribed to the v42l subdev event queue as well as to the bridge driver + * using the notify callback. The notification type for the notify callback + * will be V4L2_DEVICE_NOTIFY_EVENT. + */ +void v4l2_subdev_notify_event(struct v4l2_subdev *sd, + const struct v4l2_event *ev) +{ + v4l2_event_queue(sd->devnode, ev); + v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); diff --git a/kernel/drivers/media/v4l2-core/v4l2-trace.c b/kernel/drivers/media/v4l2-core/v4l2-trace.c new file mode 100644 index 000000000..741601054 --- /dev/null +++ b/kernel/drivers/media/v4l2-core/v4l2-trace.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_buf_done); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_buf_queue); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_dqbuf); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_v4l2_qbuf); diff --git a/kernel/drivers/media/v4l2-core/vb2-trace.c b/kernel/drivers/media/v4l2-core/vb2-trace.c new file mode 100644 index 000000000..61e74f593 --- /dev/null +++ b/kernel/drivers/media/v4l2-core/vb2-trace.c @@ -0,0 +1,9 @@ +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_done); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_queue); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_dqbuf); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_qbuf); diff --git a/kernel/drivers/media/v4l2-core/videobuf-core.c b/kernel/drivers/media/v4l2-core/videobuf-core.c index 926836d18..6c02989ee 100644 --- a/kernel/drivers/media/v4l2-core/videobuf-core.c +++ b/kernel/drivers/media/v4l2-core/videobuf-core.c @@ -576,7 +576,8 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) } if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || q->type == V4L2_BUF_TYPE_VBI_OUTPUT - || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT + || q->type == V4L2_BUF_TYPE_SDR_OUTPUT) { buf->size = b->bytesused; buf->field = b->field; buf->ts = b->timestamp; @@ -1154,6 +1155,7 @@ unsigned int videobuf_poll_stream(struct file *file, case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VBI_OUTPUT: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + case V4L2_BUF_TYPE_SDR_OUTPUT: rc = POLLOUT | POLLWRNORM; break; default: diff --git a/kernel/drivers/media/v4l2-core/videobuf2-core.c b/kernel/drivers/media/v4l2-core/videobuf2-core.c index cf9d644a8..33bdd8106 100644 --- a/kernel/drivers/media/v4l2-core/videobuf2-core.c +++ b/kernel/drivers/media/v4l2-core/videobuf2-core.c @@ -1,5 +1,5 @@ /* - * videobuf2-core.c - V4L2 driver helper framework + * videobuf2-core.c - video buffer 2 core framework * * Copyright (C) 2010 Samsung Electronics * @@ -24,164 +24,18 @@ #include #include -#include -#include -#include -#include #include -static int debug; -module_param(debug, int, 0644); +#include -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - pr_info("vb2: %s: " fmt, __func__, ## arg); \ - } while (0) +#include "videobuf2-internal.h" -#ifdef CONFIG_VIDEO_ADV_DEBUG - -/* - * If advanced debugging is on, then count how often each op is called - * successfully, which can either be per-buffer or per-queue. - * - * This makes it easy to check that the 'init' and 'cleanup' - * (and variations thereof) stay balanced. - */ - -#define log_memop(vb, op) \ - dprintk(2, "call_memop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->v4l2_buf.index, #op, \ - (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") - -#define call_memop(vb, op, args...) \ -({ \ - struct vb2_queue *_q = (vb)->vb2_queue; \ - int err; \ - \ - log_memop(vb, op); \ - err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \ - if (!err) \ - (vb)->cnt_mem_ ## op++; \ - err; \ -}) - -#define call_ptr_memop(vb, op, args...) \ -({ \ - struct vb2_queue *_q = (vb)->vb2_queue; \ - void *ptr; \ - \ - log_memop(vb, op); \ - ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL; \ - if (!IS_ERR_OR_NULL(ptr)) \ - (vb)->cnt_mem_ ## op++; \ - ptr; \ -}) - -#define call_void_memop(vb, op, args...) \ -({ \ - struct vb2_queue *_q = (vb)->vb2_queue; \ - \ - log_memop(vb, op); \ - if (_q->mem_ops->op) \ - _q->mem_ops->op(args); \ - (vb)->cnt_mem_ ## op++; \ -}) - -#define log_qop(q, op) \ - dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ - (q)->ops->op ? "" : " (nop)") - -#define call_qop(q, op, args...) \ -({ \ - int err; \ - \ - log_qop(q, op); \ - err = (q)->ops->op ? (q)->ops->op(args) : 0; \ - if (!err) \ - (q)->cnt_ ## op++; \ - err; \ -}) - -#define call_void_qop(q, op, args...) \ -({ \ - log_qop(q, op); \ - if ((q)->ops->op) \ - (q)->ops->op(args); \ - (q)->cnt_ ## op++; \ -}) - -#define log_vb_qop(vb, op, args...) \ - dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->v4l2_buf.index, #op, \ - (vb)->vb2_queue->ops->op ? "" : " (nop)") - -#define call_vb_qop(vb, op, args...) \ -({ \ - int err; \ - \ - log_vb_qop(vb, op); \ - err = (vb)->vb2_queue->ops->op ? \ - (vb)->vb2_queue->ops->op(args) : 0; \ - if (!err) \ - (vb)->cnt_ ## op++; \ - err; \ -}) - -#define call_void_vb_qop(vb, op, args...) \ -({ \ - log_vb_qop(vb, op); \ - if ((vb)->vb2_queue->ops->op) \ - (vb)->vb2_queue->ops->op(args); \ - (vb)->cnt_ ## op++; \ -}) - -#else - -#define call_memop(vb, op, args...) \ - ((vb)->vb2_queue->mem_ops->op ? \ - (vb)->vb2_queue->mem_ops->op(args) : 0) - -#define call_ptr_memop(vb, op, args...) \ - ((vb)->vb2_queue->mem_ops->op ? \ - (vb)->vb2_queue->mem_ops->op(args) : NULL) - -#define call_void_memop(vb, op, args...) \ - do { \ - if ((vb)->vb2_queue->mem_ops->op) \ - (vb)->vb2_queue->mem_ops->op(args); \ - } while (0) - -#define call_qop(q, op, args...) \ - ((q)->ops->op ? (q)->ops->op(args) : 0) - -#define call_void_qop(q, op, args...) \ - do { \ - if ((q)->ops->op) \ - (q)->ops->op(args); \ - } while (0) - -#define call_vb_qop(vb, op, args...) \ - ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0) - -#define call_void_vb_qop(vb, op, args...) \ - do { \ - if ((vb)->vb2_queue->ops->op) \ - (vb)->vb2_queue->ops->op(args); \ - } while (0) - -#endif - -/* Flags that are set by the vb2 core */ -#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ - V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED | \ - V4L2_BUF_FLAG_TIMESTAMP_MASK) -/* Output buffer flags that should be passed on to the driver */ -#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \ - V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE) +int vb2_debug; +EXPORT_SYMBOL_GPL(vb2_debug); +module_param_named(debug, vb2_debug, int, 0644); static void __vb2_queue_cancel(struct vb2_queue *q); +static void __enqueue_in_driver(struct vb2_buffer *vb); /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -190,7 +44,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; enum dma_data_direction dma_dir = - V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; void *mem_priv; int plane; @@ -208,7 +62,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) /* Associate allocator private data with this plane */ vb->planes[plane].mem_priv = mem_priv; - vb->v4l2_planes[plane].length = q->plane_sizes[plane]; + vb->planes[plane].length = q->plane_sizes[plane]; } return 0; @@ -232,8 +86,7 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { call_void_memop(vb, put, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - dprintk(3, "freed plane %d of buffer %d\n", plane, - vb->v4l2_buf.index); + dprintk(3, "freed plane %d of buffer %d\n", plane, vb->index); } } @@ -266,7 +119,9 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) call_void_memop(vb, detach_dmabuf, p->mem_priv); dma_buf_put(p->dbuf); - memset(p, 0, sizeof(*p)); + p->mem_priv = NULL; + p->dbuf = NULL; + p->dbuf_mapped = 0; } /** @@ -296,7 +151,7 @@ static void __setup_lengths(struct vb2_queue *q, unsigned int n) continue; for (plane = 0; plane < vb->num_planes; ++plane) - vb->v4l2_planes[plane].length = q->plane_sizes[plane]; + vb->planes[plane].length = q->plane_sizes[plane]; } } @@ -311,10 +166,10 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n) unsigned long off; if (q->num_buffers) { - struct v4l2_plane *p; + struct vb2_plane *p; vb = q->bufs[q->num_buffers - 1]; - p = &vb->v4l2_planes[vb->num_planes - 1]; - off = PAGE_ALIGN(p->m.mem_offset + p->length); + p = &vb->planes[vb->num_planes - 1]; + off = PAGE_ALIGN(p->m.offset + p->length); } else { off = 0; } @@ -325,12 +180,12 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n) continue; for (plane = 0; plane < vb->num_planes; ++plane) { - vb->v4l2_planes[plane].m.mem_offset = off; + vb->planes[plane].m.offset = off; dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", buffer, plane, off); - off += vb->v4l2_planes[plane].length; + off += vb->planes[plane].length; off = PAGE_ALIGN(off); } } @@ -343,7 +198,7 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n) * * Returns the number of buffers successfully allocated. */ -static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, +static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, unsigned int num_buffers, unsigned int num_planes) { unsigned int buffer; @@ -358,19 +213,15 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, break; } - /* Length stores number of planes for multiplanar buffers */ - if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) - vb->v4l2_buf.length = num_planes; - vb->state = VB2_BUF_STATE_DEQUEUED; vb->vb2_queue = q; vb->num_planes = num_planes; - vb->v4l2_buf.index = q->num_buffers + buffer; - vb->v4l2_buf.type = q->type; - vb->v4l2_buf.memory = memory; + vb->index = q->num_buffers + buffer; + vb->type = q->type; + vb->memory = memory; /* Allocate video buffer memory for the MMAP type */ - if (memory == V4L2_MEMORY_MMAP) { + if (memory == VB2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb); if (ret) { dprintk(1, "failed allocating memory for " @@ -397,7 +248,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, } __setup_lengths(q, buffer); - if (memory == V4L2_MEMORY_MMAP) + if (memory == VB2_MEMORY_MMAP) __setup_offsets(q, buffer); dprintk(1, "allocated %d buffers, %d plane(s) each\n", @@ -421,9 +272,9 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) continue; /* Free MMAP buffers or release USERPTR buffers */ - if (q->memory == V4L2_MEMORY_MMAP) + if (q->memory == VB2_MEMORY_MMAP) __vb2_buf_mem_free(vb); - else if (q->memory == V4L2_MEMORY_DMABUF) + else if (q->memory == VB2_MEMORY_DMABUF) __vb2_buf_dmabuf_put(vb); else __vb2_buf_userptr_put(vb); @@ -479,7 +330,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || q->cnt_wait_prepare != q->cnt_wait_finish; - if (unbalanced || debug) { + if (unbalanced || vb2_debug) { pr_info("vb2: counters for queue %p:%s\n", q, unbalanced ? " UNBALANCED!" : ""); pr_info("vb2: setup: %u start_streaming: %u stop_streaming: %u\n", @@ -505,7 +356,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) vb->cnt_buf_prepare != vb->cnt_buf_finish || vb->cnt_buf_init != vb->cnt_buf_cleanup; - if (unbalanced || debug) { + if (unbalanced || vb2_debug) { pr_info("vb2: counters for queue %p, buffer %d:%s\n", q, buffer, unbalanced ? " UNBALANCED!" : ""); pr_info("vb2: buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n", @@ -547,76 +398,10 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) } /** - * __verify_planes_array() - verify that the planes array passed in struct - * v4l2_buffer from userspace can be safely used - */ -static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - if (!V4L2_TYPE_IS_MULTIPLANAR(b->type)) - return 0; - - /* Is memory for copying plane information present? */ - if (NULL == b->m.planes) { - dprintk(1, "multi-planar buffer passed but " - "planes array not provided\n"); - return -EINVAL; - } - - if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) { - dprintk(1, "incorrect planes array length, " - "expected %d, got %d\n", vb->num_planes, b->length); - return -EINVAL; - } - - return 0; -} - -/** - * __verify_length() - Verify that the bytesused value for each plane fits in - * the plane length and that the data offset doesn't exceed the bytesused value. - */ -static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - unsigned int length; - unsigned int bytesused; - unsigned int plane; - - if (!V4L2_TYPE_IS_OUTPUT(b->type)) - return 0; - - if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { - for (plane = 0; plane < vb->num_planes; ++plane) { - length = (b->memory == V4L2_MEMORY_USERPTR || - b->memory == V4L2_MEMORY_DMABUF) - ? b->m.planes[plane].length - : vb->v4l2_planes[plane].length; - bytesused = b->m.planes[plane].bytesused - ? b->m.planes[plane].bytesused : length; - - if (b->m.planes[plane].bytesused > length) - return -EINVAL; - - if (b->m.planes[plane].data_offset > 0 && - b->m.planes[plane].data_offset >= bytesused) - return -EINVAL; - } - } else { - length = (b->memory == V4L2_MEMORY_USERPTR) - ? b->length : vb->v4l2_planes[0].length; - bytesused = b->bytesused ? b->bytesused : length; - - if (b->bytesused > length) - return -EINVAL; - } - - return 0; -} - -/** - * __buffer_in_use() - return true if the buffer is in use and + * vb2_buffer_in_use() - return true if the buffer is in use and * the queue cannot be freed (by the means of REQBUFS(0)) call */ -static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) +bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) { unsigned int plane; for (plane = 0; plane < vb->num_planes; ++plane) { @@ -632,6 +417,7 @@ static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) } return false; } +EXPORT_SYMBOL(vb2_buffer_in_use); /** * __buffers_in_use() - return true if any buffers on the queue are in use and @@ -641,121 +427,30 @@ static bool __buffers_in_use(struct vb2_queue *q) { unsigned int buffer; for (buffer = 0; buffer < q->num_buffers; ++buffer) { - if (__buffer_in_use(q, q->bufs[buffer])) + if (vb2_buffer_in_use(q, q->bufs[buffer])) return true; } return false; } /** - * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be - * returned to userspace - */ -static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) -{ - struct vb2_queue *q = vb->vb2_queue; - - /* Copy back data such as timestamp, flags, etc. */ - memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); - b->reserved2 = vb->v4l2_buf.reserved2; - b->reserved = vb->v4l2_buf.reserved; - - if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { - /* - * Fill in plane-related data if userspace provided an array - * for it. The caller has already verified memory and size. - */ - b->length = vb->num_planes; - memcpy(b->m.planes, vb->v4l2_planes, - b->length * sizeof(struct v4l2_plane)); - } else { - /* - * We use length and offset in v4l2_planes array even for - * single-planar buffers, but userspace does not. - */ - b->length = vb->v4l2_planes[0].length; - b->bytesused = vb->v4l2_planes[0].bytesused; - if (q->memory == V4L2_MEMORY_MMAP) - b->m.offset = vb->v4l2_planes[0].m.mem_offset; - else if (q->memory == V4L2_MEMORY_USERPTR) - b->m.userptr = vb->v4l2_planes[0].m.userptr; - else if (q->memory == V4L2_MEMORY_DMABUF) - b->m.fd = vb->v4l2_planes[0].m.fd; - } - - /* - * Clear any buffer state related flags. - */ - b->flags &= ~V4L2_BUFFER_MASK_FLAGS; - b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; - if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != - V4L2_BUF_FLAG_TIMESTAMP_COPY) { - /* - * For non-COPY timestamps, drop timestamp source bits - * and obtain the timestamp source from the queue. - */ - b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - } - - switch (vb->state) { - case VB2_BUF_STATE_QUEUED: - case VB2_BUF_STATE_ACTIVE: - b->flags |= V4L2_BUF_FLAG_QUEUED; - break; - case VB2_BUF_STATE_ERROR: - b->flags |= V4L2_BUF_FLAG_ERROR; - /* fall through */ - case VB2_BUF_STATE_DONE: - b->flags |= V4L2_BUF_FLAG_DONE; - break; - case VB2_BUF_STATE_PREPARED: - b->flags |= V4L2_BUF_FLAG_PREPARED; - break; - case VB2_BUF_STATE_PREPARING: - case VB2_BUF_STATE_DEQUEUED: - /* nothing */ - break; - } - - if (__buffer_in_use(q, vb)) - b->flags |= V4L2_BUF_FLAG_MAPPED; -} - -/** - * vb2_querybuf() - query video buffer information + * vb2_core_querybuf() - query video buffer information * @q: videobuf queue - * @b: buffer struct passed from userspace to vidioc_querybuf handler - * in driver + * @index: id number of the buffer + * @pb: buffer struct passed from userspace * * Should be called from vidioc_querybuf ioctl handler in driver. - * This function will verify the passed v4l2_buffer structure and fill the - * relevant information for the userspace. + * The passed buffer should have been verified. + * This function fills the relevant information for the userspace. * * The return values from this function are intended to be directly returned * from vidioc_querybuf handler in driver. */ -int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) +int vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb) { - struct vb2_buffer *vb; - int ret; - - if (b->type != q->type) { - dprintk(1, "wrong buffer type\n"); - return -EINVAL; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); - return -EINVAL; - } - vb = q->bufs[b->index]; - ret = __verify_planes_array(vb, b); - if (!ret) - __fill_v4l2_buffer(vb, b); - return ret; + return call_bufop(q, fill_user_buffer, q->bufs[index], pb); } -EXPORT_SYMBOL(vb2_querybuf); +EXPORT_SYMBOL_GPL(vb2_core_querybuf); /** * __verify_userptr_ops() - verify that all memory operations required for @@ -798,14 +493,14 @@ static int __verify_dmabuf_ops(struct vb2_queue *q) } /** - * __verify_memory_type() - Check whether the memory type and buffer type + * vb2_verify_memory_type() - Check whether the memory type and buffer type * passed to a buffer operation are compatible with the queue. */ -static int __verify_memory_type(struct vb2_queue *q, - enum v4l2_memory memory, enum v4l2_buf_type type) +int vb2_verify_memory_type(struct vb2_queue *q, + enum vb2_memory memory, unsigned int type) { - if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR && - memory != V4L2_MEMORY_DMABUF) { + if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR && + memory != VB2_MEMORY_DMABUF) { dprintk(1, "unsupported memory type\n"); return -EINVAL; } @@ -819,17 +514,17 @@ static int __verify_memory_type(struct vb2_queue *q, * Make sure all the required memory ops for given memory type * are available. */ - if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { + if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) { dprintk(1, "MMAP for current setup unsupported\n"); return -EINVAL; } - if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { + if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) { dprintk(1, "USERPTR for current setup unsupported\n"); return -EINVAL; } - if (memory == V4L2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { + if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { dprintk(1, "DMABUF for current setup unsupported\n"); return -EINVAL; } @@ -845,11 +540,13 @@ static int __verify_memory_type(struct vb2_queue *q, } return 0; } +EXPORT_SYMBOL(vb2_verify_memory_type); /** - * __reqbufs() - Initiate streaming + * vb2_core_reqbufs() - Initiate streaming * @q: videobuf2 queue - * @req: struct passed from userspace to vidioc_reqbufs handler in driver + * @memory: memory type + * @count: requested buffer count * * Should be called from vidioc_reqbufs ioctl handler of a driver. * This function: @@ -869,7 +566,8 @@ static int __verify_memory_type(struct vb2_queue *q, * The return values from this function are intended to be directly returned * from vidioc_reqbufs handler in driver. */ -static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) +int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, + unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; int ret; @@ -879,13 +577,13 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EBUSY; } - if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { + if (*count == 0 || q->num_buffers != 0 || q->memory != memory) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. */ mutex_lock(&q->mmap_lock); - if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { + if (q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) { mutex_unlock(&q->mmap_lock); dprintk(1, "memory in use, cannot free\n"); return -EBUSY; @@ -906,18 +604,18 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * In case of REQBUFS(0) return immediately without calling * driver's queue_setup() callback and allocating resources. */ - if (req->count == 0) + if (*count == 0) return 0; } /* * Make sure the requested values and current defaults are sane. */ - num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); + num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME); num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed); memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); - q->memory = req->memory; + q->memory = memory; /* * Ask the driver how many buffers and planes per buffer it requires. @@ -929,7 +627,8 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return ret; /* Finally, allocate buffers and video memory */ - allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes); + allocated_buffers = + __vb2_queue_alloc(q, memory, num_buffers, num_planes); if (allocated_buffers == 0) { dprintk(1, "memory allocation failed\n"); return -ENOMEM; @@ -978,31 +677,19 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * Return the number of successfully allocated buffers * to the userspace. */ - req->count = allocated_buffers; - q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); + *count = allocated_buffers; + q->waiting_for_buffers = !q->is_output; return 0; } +EXPORT_SYMBOL_GPL(vb2_core_reqbufs); /** - * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and - * type values. - * @q: videobuf2 queue - * @req: struct passed from userspace to vidioc_reqbufs handler in driver - */ -int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) -{ - int ret = __verify_memory_type(q, req->memory, req->type); - - return ret ? ret : __reqbufs(q, req); -} -EXPORT_SYMBOL_GPL(vb2_reqbufs); - -/** - * __create_bufs() - Allocate buffers and any required auxiliary structs + * vb2_core_create_bufs() - Allocate buffers and any required auxiliary structs * @q: videobuf2 queue - * @create: creation parameters, passed from userspace to vidioc_create_bufs - * handler in driver + * @memory: memory type + * @count: requested buffer count + * @parg: parameter passed to device driver * * Should be called from vidioc_create_bufs ioctl handler of a driver. * This function: @@ -1013,12 +700,13 @@ EXPORT_SYMBOL_GPL(vb2_reqbufs); * The return values from this function are intended to be directly returned * from vidioc_create_bufs handler in driver. */ -static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, + unsigned int *count, const void *parg) { unsigned int num_planes = 0, num_buffers, allocated_buffers; int ret; - if (q->num_buffers == VIDEO_MAX_FRAME) { + if (q->num_buffers == VB2_MAX_FRAME) { dprintk(1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } @@ -1026,23 +714,23 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create if (!q->num_buffers) { memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); - q->memory = create->memory; - q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); + q->memory = memory; + q->waiting_for_buffers = !q->is_output; } - num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers); + num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); /* * Ask the driver, whether the requested number of buffers, planes per * buffer and their sizes are acceptable */ - ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + ret = call_qop(q, queue_setup, q, parg, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); if (ret) return ret; /* Finally, allocate buffers and video memory */ - allocated_buffers = __vb2_queue_alloc(q, create->memory, num_buffers, + allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes); if (allocated_buffers == 0) { dprintk(1, "memory allocation failed\n"); @@ -1059,7 +747,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create * q->num_buffers contains the total number of buffers, that the * queue driver has set up */ - ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + ret = call_qop(q, queue_setup, q, parg, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); if (!ret && allocated_buffers < num_buffers) @@ -1089,28 +777,11 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create * Return the number of successfully allocated buffers * to the userspace. */ - create->count = allocated_buffers; + *count = allocated_buffers; return 0; } - -/** - * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the - * memory and type values. - * @q: videobuf2 queue - * @create: creation parameters, passed from userspace to vidioc_create_bufs - * handler in driver - */ -int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) -{ - int ret = __verify_memory_type(q, create->memory, create->format.type); - - create->index = q->num_buffers; - if (create->count == 0) - return ret != -EBUSY ? ret : 0; - return ret ? ret : __create_bufs(q, create); -} -EXPORT_SYMBOL_GPL(vb2_create_bufs); +EXPORT_SYMBOL_GPL(vb2_core_create_bufs); /** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane @@ -1153,8 +824,9 @@ EXPORT_SYMBOL_GPL(vb2_plane_cookie); /** * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished * @vb: vb2_buffer returned from the driver - * @state: either VB2_BUF_STATE_DONE if the operation finished successfully - * or VB2_BUF_STATE_ERROR if the operation finished with an error. + * @state: either VB2_BUF_STATE_DONE if the operation finished successfully, + * VB2_BUF_STATE_ERROR if the operation finished with an error or + * VB2_BUF_STATE_QUEUED if the driver wants to requeue buffers. * If start_streaming fails then it should return buffers with state * VB2_BUF_STATE_QUEUED to put them back into the queue. * @@ -1180,7 +852,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) if (WARN_ON(state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR && - state != VB2_BUF_STATE_QUEUED)) + state != VB2_BUF_STATE_QUEUED && + state != VB2_BUF_STATE_REQUEUEING)) state = VB2_BUF_STATE_ERROR; #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1191,25 +864,38 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) vb->cnt_buf_done++; #endif dprintk(4, "done processing on buffer %d, state: %d\n", - vb->v4l2_buf.index, state); + vb->index, state); /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - /* Add the buffer to the done buffers list */ spin_lock_irqsave(&q->done_lock, flags); - vb->state = state; - if (state != VB2_BUF_STATE_QUEUED) + if (state == VB2_BUF_STATE_QUEUED || + state == VB2_BUF_STATE_REQUEUEING) { + vb->state = VB2_BUF_STATE_QUEUED; + } else { + /* Add the buffer to the done buffers list */ list_add_tail(&vb->done_entry, &q->done_list); + vb->state = state; + } atomic_dec(&q->owned_by_drv_count); spin_unlock_irqrestore(&q->done_lock, flags); - if (state == VB2_BUF_STATE_QUEUED) - return; + trace_vb2_buf_done(q, vb); - /* Inform any processes that may be waiting for buffers */ - wake_up(&q->done_wq); + switch (state) { + case VB2_BUF_STATE_QUEUED: + return; + case VB2_BUF_STATE_REQUEUEING: + if (q->start_streaming_called) + __enqueue_in_driver(vb); + return; + default: + /* Inform any processes that may be waiting for buffers */ + wake_up(&q->done_wq); + break; + } } EXPORT_SYMBOL_GPL(vb2_buffer_done); @@ -1237,182 +923,41 @@ void vb2_discard_done(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(vb2_discard_done); -static void vb2_warn_zero_bytesused(struct vb2_buffer *vb) -{ - static bool __check_once __read_mostly; - - if (__check_once) - return; - - __check_once = true; - __WARN(); - - pr_warn_once("use of bytesused == 0 is deprecated and will be removed in the future,\n"); - if (vb->vb2_queue->allow_zero_bytesused) - pr_warn_once("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n"); - else - pr_warn_once("use the actual size instead.\n"); -} - -/** - * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a - * v4l2_buffer by the userspace. The caller has already verified that struct - * v4l2_buffer has a valid number of planes. - */ -static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, - struct v4l2_plane *v4l2_planes) -{ - unsigned int plane; - - if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { - if (b->memory == V4L2_MEMORY_USERPTR) { - for (plane = 0; plane < vb->num_planes; ++plane) { - v4l2_planes[plane].m.userptr = - b->m.planes[plane].m.userptr; - v4l2_planes[plane].length = - b->m.planes[plane].length; - } - } - if (b->memory == V4L2_MEMORY_DMABUF) { - for (plane = 0; plane < vb->num_planes; ++plane) { - v4l2_planes[plane].m.fd = - b->m.planes[plane].m.fd; - v4l2_planes[plane].length = - b->m.planes[plane].length; - } - } - - /* Fill in driver-provided information for OUTPUT types */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - /* - * Will have to go up to b->length when API starts - * accepting variable number of planes. - * - * If bytesused == 0 for the output buffer, then fall - * back to the full buffer size. In that case - * userspace clearly never bothered to set it and - * it's a safe assumption that they really meant to - * use the full plane sizes. - * - * Some drivers, e.g. old codec drivers, use bytesused == 0 - * as a way to indicate that streaming is finished. - * In that case, the driver should use the - * allow_zero_bytesused flag to keep old userspace - * applications working. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - struct v4l2_plane *pdst = &v4l2_planes[plane]; - struct v4l2_plane *psrc = &b->m.planes[plane]; - - if (psrc->bytesused == 0) - vb2_warn_zero_bytesused(vb); - - if (vb->vb2_queue->allow_zero_bytesused) - pdst->bytesused = psrc->bytesused; - else - pdst->bytesused = psrc->bytesused ? - psrc->bytesused : pdst->length; - pdst->data_offset = psrc->data_offset; - } - } - } else { - /* - * Single-planar buffers do not use planes array, - * so fill in relevant v4l2_buffer struct fields instead. - * In videobuf we use our internal V4l2_planes struct for - * single-planar buffers as well, for simplicity. - * - * If bytesused == 0 for the output buffer, then fall back - * to the full buffer size as that's a sensible default. - * - * Some drivers, e.g. old codec drivers, use bytesused == 0 as - * a way to indicate that streaming is finished. In that case, - * the driver should use the allow_zero_bytesused flag to keep - * old userspace applications working. - */ - if (b->memory == V4L2_MEMORY_USERPTR) { - v4l2_planes[0].m.userptr = b->m.userptr; - v4l2_planes[0].length = b->length; - } - - if (b->memory == V4L2_MEMORY_DMABUF) { - v4l2_planes[0].m.fd = b->m.fd; - v4l2_planes[0].length = b->length; - } - - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - if (b->bytesused == 0) - vb2_warn_zero_bytesused(vb); - - if (vb->vb2_queue->allow_zero_bytesused) - v4l2_planes[0].bytesused = b->bytesused; - else - v4l2_planes[0].bytesused = b->bytesused ? - b->bytesused : v4l2_planes[0].length; - } else - v4l2_planes[0].bytesused = 0; - - } - - /* Zero flags that the vb2 core handles */ - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; - if ((vb->vb2_queue->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != - V4L2_BUF_FLAG_TIMESTAMP_COPY || !V4L2_TYPE_IS_OUTPUT(b->type)) { - /* - * Non-COPY timestamps and non-OUTPUT queues will get - * their timestamp and timestamp source flags from the - * queue. - */ - vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - } - - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - /* - * For output buffers mask out the timecode flag: - * this will be handled later in vb2_internal_qbuf(). - * The 'field' is valid metadata for this output buffer - * and so that needs to be copied here. - */ - vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMECODE; - vb->v4l2_buf.field = b->field; - } else { - /* Zero any output buffer flags as this is a capture buffer */ - vb->v4l2_buf.flags &= ~V4L2_BUFFER_OUT_FLAGS; - } -} - /** * __qbuf_mmap() - handle qbuf of an MMAP buffer */ -static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) +static int __qbuf_mmap(struct vb2_buffer *vb, const void *pb) { - __fill_vb2_buffer(vb, b, vb->v4l2_planes); - return call_vb_qop(vb, buf_prepare, vb); + int ret = call_bufop(vb->vb2_queue, fill_vb2_buffer, + vb, pb, vb->planes); + return ret ? ret : call_vb_qop(vb, buf_prepare, vb); } /** * __qbuf_userptr() - handle qbuf of a USERPTR buffer */ -static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) +static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb) { - struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct vb2_plane planes[VB2_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; void *mem_priv; unsigned int plane; int ret; enum dma_data_direction dma_dir = - V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; bool reacquired = vb->planes[0].mem_priv == NULL; memset(planes, 0, sizeof(planes[0]) * vb->num_planes); /* Copy relevant information provided by the userspace */ - __fill_vb2_buffer(vb, b, planes); + ret = call_bufop(vb->vb2_queue, fill_vb2_buffer, vb, pb, planes); + if (ret) + return ret; for (plane = 0; plane < vb->num_planes; ++plane) { /* Skip the plane if already verified */ - if (vb->v4l2_planes[plane].m.userptr && - vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr - && vb->v4l2_planes[plane].length == planes[plane].length) + if (vb->planes[plane].m.userptr && + vb->planes[plane].m.userptr == planes[plane].m.userptr + && vb->planes[plane].length == planes[plane].length) continue; dprintk(3, "userspace address for plane %d changed, " @@ -1438,7 +983,10 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) } vb->planes[plane].mem_priv = NULL; - memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane)); + vb->planes[plane].bytesused = 0; + vb->planes[plane].length = 0; + vb->planes[plane].m.userptr = 0; + vb->planes[plane].data_offset = 0; /* Acquire each plane's memory */ mem_priv = call_ptr_memop(vb, get_userptr, q->alloc_ctx[plane], @@ -1457,8 +1005,12 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) * Now that everything is in order, copy relevant information * provided by userspace. */ - for (plane = 0; plane < vb->num_planes; ++plane) - vb->v4l2_planes[plane] = planes[plane]; + for (plane = 0; plane < vb->num_planes; ++plane) { + vb->planes[plane].bytesused = planes[plane].bytesused; + vb->planes[plane].length = planes[plane].length; + vb->planes[plane].m.userptr = planes[plane].m.userptr; + vb->planes[plane].data_offset = planes[plane].data_offset; + } if (reacquired) { /* @@ -1485,10 +1037,11 @@ err: /* In case of errors, release planes that were already acquired */ for (plane = 0; plane < vb->num_planes; ++plane) { if (vb->planes[plane].mem_priv) - call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); + call_void_memop(vb, put_userptr, + vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - vb->v4l2_planes[plane].m.userptr = 0; - vb->v4l2_planes[plane].length = 0; + vb->planes[plane].m.userptr = 0; + vb->planes[plane].length = 0; } return ret; @@ -1497,20 +1050,22 @@ err: /** * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer */ -static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) +static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb) { - struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct vb2_plane planes[VB2_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; void *mem_priv; unsigned int plane; int ret; enum dma_data_direction dma_dir = - V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; bool reacquired = vb->planes[0].mem_priv == NULL; memset(planes, 0, sizeof(planes[0]) * vb->num_planes); /* Copy relevant information provided by the userspace */ - __fill_vb2_buffer(vb, b, planes); + ret = call_bufop(vb->vb2_queue, fill_vb2_buffer, vb, pb, planes); + if (ret) + return ret; for (plane = 0; plane < vb->num_planes; ++plane) { struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); @@ -1535,7 +1090,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) /* Skip the plane if already verified */ if (dbuf == vb->planes[plane].dbuf && - vb->v4l2_planes[plane].length == planes[plane].length) { + vb->planes[plane].length == planes[plane].length) { dma_buf_put(dbuf); continue; } @@ -1549,11 +1104,15 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) /* Release previously acquired memory if present */ __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); - memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane)); + vb->planes[plane].bytesused = 0; + vb->planes[plane].length = 0; + vb->planes[plane].m.fd = 0; + vb->planes[plane].data_offset = 0; /* Acquire each plane's memory */ - mem_priv = call_ptr_memop(vb, attach_dmabuf, q->alloc_ctx[plane], - dbuf, planes[plane].length, dma_dir); + mem_priv = call_ptr_memop(vb, attach_dmabuf, + q->alloc_ctx[plane], dbuf, planes[plane].length, + dma_dir); if (IS_ERR(mem_priv)) { dprintk(1, "failed to attach dmabuf\n"); ret = PTR_ERR(mem_priv); @@ -1583,8 +1142,12 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) * Now that everything is in order, copy relevant information * provided by userspace. */ - for (plane = 0; plane < vb->num_planes; ++plane) - vb->v4l2_planes[plane] = planes[plane]; + for (plane = 0; plane < vb->num_planes; ++plane) { + vb->planes[plane].bytesused = planes[plane].bytesused; + vb->planes[plane].length = planes[plane].length; + vb->planes[plane].m.fd = planes[plane].m.fd; + vb->planes[plane].data_offset = planes[plane].data_offset; + } if (reacquired) { /* @@ -1624,6 +1187,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) 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); @@ -1631,51 +1196,27 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) call_void_vb_qop(vb, buf_queue, vb); } -static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) +static int __buf_prepare(struct vb2_buffer *vb, const void *pb) { struct vb2_queue *q = vb->vb2_queue; int ret; - ret = __verify_length(vb, b); - if (ret < 0) { - dprintk(1, "plane parameters verification failed: %d\n", ret); - return ret; - } - if (b->field == V4L2_FIELD_ALTERNATE && V4L2_TYPE_IS_OUTPUT(q->type)) { - /* - * If the format's field is ALTERNATE, then the buffer's field - * should be either TOP or BOTTOM, not ALTERNATE since that - * makes no sense. The driver has to know whether the - * buffer represents a top or a bottom field in order to - * program any DMA correctly. Using ALTERNATE is wrong, since - * that just says that it is either a top or a bottom field, - * but not which of the two it is. - */ - dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); - return -EINVAL; - } - if (q->error) { dprintk(1, "fatal error occurred on queue\n"); return -EIO; } vb->state = VB2_BUF_STATE_PREPARING; - vb->v4l2_buf.timestamp.tv_sec = 0; - vb->v4l2_buf.timestamp.tv_usec = 0; - vb->v4l2_buf.sequence = 0; switch (q->memory) { - case V4L2_MEMORY_MMAP: - ret = __qbuf_mmap(vb, b); + case VB2_MEMORY_MMAP: + ret = __qbuf_mmap(vb, pb); break; - case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); - ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); + case VB2_MEMORY_USERPTR: + ret = __qbuf_userptr(vb, pb); break; - case V4L2_MEMORY_DMABUF: - ret = __qbuf_dmabuf(vb, b); + case VB2_MEMORY_DMABUF: + ret = __qbuf_dmabuf(vb, pb); break; default: WARN(1, "Invalid queue type\n"); @@ -1689,79 +1230,48 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return ret; } -static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, - const char *opname) -{ - if (b->type != q->type) { - dprintk(1, "%s: invalid buffer type\n", opname); - return -EINVAL; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "%s: buffer index out of range\n", opname); - return -EINVAL; - } - - if (q->bufs[b->index] == NULL) { - /* Should never happen */ - dprintk(1, "%s: buffer is NULL\n", opname); - return -EINVAL; - } - - if (b->memory != q->memory) { - dprintk(1, "%s: invalid memory type\n", opname); - return -EINVAL; - } - - return __verify_planes_array(q->bufs[b->index], b); -} - /** - * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel + * vb2_core_prepare_buf() - Pass ownership of a buffer from userspace + * to the kernel * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_prepare_buf + * @index: id number of the buffer + * @pb: buffer structure passed from userspace to vidioc_prepare_buf * handler in driver * * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, + * The passed buffer should have been verified. + * This function calls buf_prepare callback in the driver (if provided), + * in which driver-specific buffer initialization can be performed, * * The return values from this function are intended to be directly returned * from vidioc_prepare_buf handler in driver. */ -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) { struct vb2_buffer *vb; int ret; - if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); - return -EBUSY; - } - - ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); - if (ret) - return ret; - - vb = q->bufs[b->index]; + vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(1, "invalid buffer state %d\n", vb->state); return -EINVAL; } - ret = __buf_prepare(vb, b); - if (!ret) { - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + ret = __buf_prepare(vb, pb); + if (ret) + return ret; + + /* Fill buffer information for the userspace */ + ret = call_bufop(q, fill_user_buffer, vb, pb); + if (ret) + return ret; + + dprintk(1, "prepare of buffer %d succeeded\n", vb->index); - dprintk(1, "prepare of buffer %d succeeded\n", vb->v4l2_buf.index); - } return ret; } -EXPORT_SYMBOL_GPL(vb2_prepare_buf); +EXPORT_SYMBOL_GPL(vb2_core_prepare_buf); /** * vb2_start_streaming() - Attempt to start streaming. @@ -1826,19 +1336,34 @@ static int vb2_start_streaming(struct vb2_queue *q) return ret; } -static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +/** + * vb2_core_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @index: id number of the buffer + * @pb: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * The passed buffer should have been verified. + * This function: + * 1) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 2) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb) { - int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); struct vb2_buffer *vb; + int ret; - if (ret) - return ret; - - vb = q->bufs[b->index]; + vb = q->bufs[index]; switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: - ret = __buf_prepare(vb, b); + ret = __buf_prepare(vb, pb); if (ret) return ret; break; @@ -1860,18 +1385,10 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) q->queued_count++; q->waiting_for_buffers = false; vb->state = VB2_BUF_STATE_QUEUED; - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - /* - * For output buffers copy the timestamp if needed, - * and the timecode field and flag if needed. - */ - if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == - V4L2_BUF_FLAG_TIMESTAMP_COPY) - vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; - if (b->flags & V4L2_BUF_FLAG_TIMECODE) - vb->v4l2_buf.timecode = b->timecode; - } + + call_bufop(q, set_timestamp, vb, pb); + + trace_vb2_qbuf(q, vb); /* * If already streaming, give the buffer to driver for processing. @@ -1881,7 +1398,9 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) __enqueue_in_driver(vb); /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + ret = call_bufop(q, fill_user_buffer, vb, pb); + if (ret) + return ret; /* * If streamon has been called, and we haven't yet called @@ -1896,37 +1415,10 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) return ret; } - dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); + dprintk(1, "qbuf of buffer %d succeeded\n", vb->index); return 0; } - -/** - * vb2_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) -{ - if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); - return -EBUSY; - } - - return vb2_internal_qbuf(q, b); -} -EXPORT_SYMBOL_GPL(vb2_qbuf); +EXPORT_SYMBOL_GPL(vb2_core_qbuf); /** * __vb2_wait_for_done_vb() - wait for a buffer to become available @@ -1958,6 +1450,11 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) return -EIO; } + if (q->last_buffer_dequeued) { + dprintk(3, "last buffer dequeued already, will not wait for buffers\n"); + return -EPIPE; + } + if (!list_empty(&q->done_list)) { /* * Found a buffer that we were waiting for. @@ -2005,7 +1502,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * Will sleep if required for nonblocking == false. */ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, - struct v4l2_buffer *b, int nonblocking) + int nonblocking) { unsigned long flags; int ret; @@ -2026,10 +1523,10 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, /* * Only remove the buffer from done_list if v4l2_buffer can handle all * the planes. + * Verifying planes is NOT necessary since it already has been checked + * before the buffer is queued/prepared. So it can never fail. */ - ret = __verify_planes_array(*vb, b); - if (!ret) - list_del(&(*vb)->done_entry); + list_del(&(*vb)->done_entry); spin_unlock_irqrestore(&q->done_lock, flags); return ret; @@ -2072,7 +1569,7 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) vb->state = VB2_BUF_STATE_DEQUEUED; /* unmap DMABUF buffer */ - if (q->memory == V4L2_MEMORY_DMABUF) + if (q->memory == VB2_MEMORY_DMABUF) for (i = 0; i < vb->num_planes; ++i) { if (!vb->planes[i].dbuf_mapped) continue; @@ -2081,16 +1578,33 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) } } -static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) -{ - struct vb2_buffer *vb = NULL; - int ret; - - if (b->type != q->type) { - dprintk(1, "invalid buffer type\n"); - return -EINVAL; - } - ret = __vb2_get_done_vb(q, &vb, b, nonblocking); +/** + * vb2_dqbuf() - Dequeue a buffer to the userspace + * @q: videobuf2 queue + * @pb: buffer structure passed from userspace to vidioc_dqbuf handler + * in driver + * @nonblocking: if true, this call will not sleep waiting for a buffer if no + * buffers ready for dequeuing are present. Normally the driver + * would be passing (file->f_flags & O_NONBLOCK) here + * + * Should be called from vidioc_dqbuf ioctl handler of a driver. + * The passed buffer should have been verified. + * This function: + * 1) calls buf_finish callback in the driver (if provided), in which + * driver can perform any additional operations that may be required before + * returning the buffer to userspace, such as cache sync, + * 2) the buffer struct members are filled with relevant information for + * the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_dqbuf handler in driver. + */ +int vb2_core_dqbuf(struct vb2_queue *q, void *pb, bool nonblocking) +{ + struct vb2_buffer *vb = NULL; + int ret; + + ret = __vb2_get_done_vb(q, &vb, nonblocking); if (ret < 0) return ret; @@ -2109,49 +1623,26 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n call_void_vb_qop(vb, buf_finish, vb); /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + ret = call_bufop(q, fill_user_buffer, vb, pb); + if (ret) + return ret; + /* Remove from videobuf queue */ list_del(&vb->queued_entry); q->queued_count--; + + trace_vb2_dqbuf(q, vb); + /* go back to dequeued state */ __vb2_dqbuf(vb); dprintk(1, "dqbuf of buffer %d, with state %d\n", - vb->v4l2_buf.index, vb->state); + vb->index, vb->state); return 0; -} -/** - * vb2_dqbuf() - Dequeue a buffer to the userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_dqbuf handler - * in driver - * @nonblocking: if true, this call will not sleep waiting for a buffer if no - * buffers ready for dequeuing are present. Normally the driver - * would be passing (file->f_flags & O_NONBLOCK) here - * - * Should be called from vidioc_dqbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_finish callback in the driver (if provided), in which - * driver can perform any additional operations that may be required before - * returning the buffer to userspace, such as cache sync, - * 3) the buffer struct members are filled with relevant information for - * the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_dqbuf handler in driver. - */ -int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) -{ - if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); - return -EBUSY; - } - return vb2_internal_dqbuf(q, b, nonblocking); } -EXPORT_SYMBOL_GPL(vb2_dqbuf); +EXPORT_SYMBOL_GPL(vb2_core_dqbuf); /** * __vb2_queue_cancel() - cancel and stop (pause) streaming @@ -2221,7 +1712,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) } } -static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) +int vb2_core_streamon(struct vb2_queue *q, unsigned int type) { int ret; @@ -2263,6 +1754,7 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) dprintk(3, "successful\n"); return 0; } +EXPORT_SYMBOL_GPL(vb2_core_streamon); /** * vb2_queue_error() - signal a fatal error on the queue @@ -2285,30 +1777,7 @@ void vb2_queue_error(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(vb2_queue_error); -/** - * vb2_streamon - start streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamon handler - * - * Should be called from vidioc_streamon handler of a driver. - * This function: - * 1) verifies current state - * 2) passes any previously queued buffers to the driver and starts streaming - * - * The return values from this function are intended to be directly returned - * from vidioc_streamon handler in the driver. - */ -int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) -{ - if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); - return -EBUSY; - } - return vb2_internal_streamon(q, type); -} -EXPORT_SYMBOL_GPL(vb2_streamon); - -static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) { if (type != q->type) { dprintk(1, "invalid stream type\n"); @@ -2325,36 +1794,13 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) * their normal dequeued state. */ __vb2_queue_cancel(q); - q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); + q->waiting_for_buffers = !q->is_output; + q->last_buffer_dequeued = false; dprintk(3, "successful\n"); return 0; } - -/** - * vb2_streamoff - stop streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamoff handler - * - * Should be called from vidioc_streamoff handler of a driver. - * This function: - * 1) verifies current state, - * 2) stop streaming and dequeues any queued buffers, including those previously - * passed to the driver (after waiting for the driver to finish). - * - * This call can be used for pausing playback. - * The return values from this function are intended to be directly returned - * from vidioc_streamoff handler in the driver - */ -int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) -{ - if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); - return -EBUSY; - } - return vb2_internal_streamoff(q, type); -} -EXPORT_SYMBOL_GPL(vb2_streamoff); +EXPORT_SYMBOL_GPL(vb2_core_streamoff); /** * __find_plane_by_offset() - find plane associated with the given offset off @@ -2374,7 +1820,7 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, vb = q->bufs[buffer]; for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->v4l2_planes[plane].m.mem_offset == off) { + if (vb->planes[plane].m.offset == off) { *_buffer = buffer; *_plane = plane; return 0; @@ -2386,22 +1832,27 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, } /** - * vb2_expbuf() - Export a buffer as a file descriptor + * vb2_core_expbuf() - Export a buffer as a file descriptor * @q: videobuf2 queue - * @eb: export buffer structure passed from userspace to vidioc_expbuf - * handler in driver + * @fd: file descriptor associated with DMABUF (set by driver) * + * @type: buffer type + * @index: id number of the buffer + * @plane: index of the plane to be exported, 0 for single plane queues + * @flags: flags for newly created file, currently only O_CLOEXEC is + * supported, refer to manual of open syscall for more details * * The return values from this function are intended to be directly returned * from vidioc_expbuf handler in driver. */ -int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) +int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, + unsigned int index, unsigned int plane, unsigned int flags) { struct vb2_buffer *vb = NULL; struct vb2_plane *vb_plane; int ret; struct dma_buf *dbuf; - if (q->memory != V4L2_MEMORY_MMAP) { + if (q->memory != VB2_MEMORY_MMAP) { dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2411,24 +1862,24 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EINVAL; } - if (eb->flags & ~(O_CLOEXEC | O_ACCMODE)) { + if (flags & ~(O_CLOEXEC | O_ACCMODE)) { dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } - if (eb->type != q->type) { + if (type != q->type) { dprintk(1, "invalid buffer type\n"); return -EINVAL; } - if (eb->index >= q->num_buffers) { + if (index >= q->num_buffers) { dprintk(1, "buffer index out of range\n"); return -EINVAL; } - vb = q->bufs[eb->index]; + vb = q->bufs[index]; - if (eb->plane >= vb->num_planes) { + if (plane >= vb->num_planes) { dprintk(1, "buffer plane out of range\n"); return -EINVAL; } @@ -2438,30 +1889,31 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EBUSY; } - vb_plane = &vb->planes[eb->plane]; + vb_plane = &vb->planes[plane]; - dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE); + dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, + flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { dprintk(1, "failed to export buffer %d, plane %d\n", - eb->index, eb->plane); + index, plane); return -EINVAL; } - ret = dma_buf_fd(dbuf, eb->flags & ~O_ACCMODE); + ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { dprintk(3, "buffer %d, plane %d failed to export (%d)\n", - eb->index, eb->plane, ret); + index, plane, ret); dma_buf_put(dbuf); return ret; } dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", - eb->index, eb->plane, ret); - eb->fd = ret; + index, plane, ret); + *fd = ret; return 0; } -EXPORT_SYMBOL_GPL(vb2_expbuf); +EXPORT_SYMBOL_GPL(vb2_core_expbuf); /** * vb2_mmap() - map video buffers into application address space @@ -2490,7 +1942,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) int ret; unsigned long length; - if (q->memory != V4L2_MEMORY_MMAP) { + if (q->memory != VB2_MEMORY_MMAP) { dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2502,7 +1954,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) dprintk(1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } - if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (q->is_output) { if (!(vma->vm_flags & VM_WRITE)) { dprintk(1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; @@ -2532,7 +1984,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * The buffer length was page_aligned at __vb2_buf_mem_alloc(), * so, we need to do the same here. */ - length = PAGE_ALIGN(vb->v4l2_planes[plane].length); + length = PAGE_ALIGN(vb->planes[plane].length); if (length < (vma->vm_end - vma->vm_start)) { dprintk(1, "MMAP invalid, as it would overflow buffer length\n"); @@ -2563,7 +2015,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, void *vaddr; int ret; - if (q->memory != V4L2_MEMORY_MMAP) { + if (q->memory != VB2_MEMORY_MMAP) { dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2583,115 +2035,8 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); #endif -static int __vb2_init_fileio(struct vb2_queue *q, int read); -static int __vb2_cleanup_fileio(struct vb2_queue *q); - /** - * vb2_poll() - implements poll userspace operation - * @q: videobuf2 queue - * @file: file argument passed to the poll file operation handler - * @wait: wait argument passed to the poll file operation handler - * - * This function implements poll file operation handler for a driver. - * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will - * be informed that the file descriptor of a video device is available for - * reading. - * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor - * will be reported as available for writing. - * - * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any - * pending events. - * - * The return values from this function are intended to be directly returned - * from poll handler in driver. - */ -unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) -{ - struct video_device *vfd = video_devdata(file); - unsigned long req_events = poll_requested_events(wait); - struct vb2_buffer *vb = NULL; - unsigned int res = 0; - unsigned long flags; - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - struct v4l2_fh *fh = file->private_data; - - if (v4l2_event_pending(fh)) - res = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->wait, wait); - } - - if (!V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLIN | POLLRDNORM))) - return res; - if (V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLOUT | POLLWRNORM))) - return res; - - /* - * Start file I/O emulator only if streaming API has not been used yet. - */ - if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && - (req_events & (POLLIN | POLLRDNORM))) { - if (__vb2_init_fileio(q, 1)) - return res | POLLERR; - } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && - (req_events & (POLLOUT | POLLWRNORM))) { - if (__vb2_init_fileio(q, 0)) - return res | POLLERR; - /* - * Write to OUTPUT queue can be done immediately. - */ - return res | POLLOUT | POLLWRNORM; - } - } - - /* - * There is nothing to wait for if the queue isn't streaming, or if the - * error flag is set. - */ - if (!vb2_is_streaming(q) || q->error) - return res | POLLERR; - /* - * For compatibility with vb1: if QBUF hasn't been called yet, then - * return POLLERR as well. This only affects capture queues, output - * queues will always initialize waiting_for_buffers to false. - */ - if (q->waiting_for_buffers) - return res | POLLERR; - - /* - * For output streams you can write as long as there are fewer buffers - * queued than there are buffers available. - */ - if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers) - return res | POLLOUT | POLLWRNORM; - - if (list_empty(&q->done_list)) - poll_wait(file, &q->done_wq, wait); - - /* - * Take first buffer available for dequeuing. - */ - spin_lock_irqsave(&q->done_lock, flags); - if (!list_empty(&q->done_list)) - vb = list_first_entry(&q->done_list, struct vb2_buffer, - done_entry); - spin_unlock_irqrestore(&q->done_lock, flags); - - if (vb && (vb->state == VB2_BUF_STATE_DONE - || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? - res | POLLOUT | POLLWRNORM : - res | POLLIN | POLLRDNORM; - } - return res; -} -EXPORT_SYMBOL_GPL(vb2_poll); - -/** - * vb2_queue_init() - initialize a videobuf2 queue + * vb2_core_queue_init() - initialize a videobuf2 queue * @q: videobuf2 queue; this structure should be allocated in driver * * The vb2_queue structure should be allocated by the driver. The driver is @@ -2701,7 +2046,7 @@ EXPORT_SYMBOL_GPL(vb2_poll); * to the struct vb2_queue description in include/media/videobuf2-core.h * for more information. */ -int vb2_queue_init(struct vb2_queue *q) +int vb2_core_queue_init(struct vb2_queue *q) { /* * Sanity check @@ -2712,16 +2057,9 @@ int vb2_queue_init(struct vb2_queue *q) WARN_ON(!q->type) || WARN_ON(!q->io_modes) || WARN_ON(!q->ops->queue_setup) || - WARN_ON(!q->ops->buf_queue) || - WARN_ON(q->timestamp_flags & - ~(V4L2_BUF_FLAG_TIMESTAMP_MASK | - V4L2_BUF_FLAG_TSTAMP_SRC_MASK))) + WARN_ON(!q->ops->buf_queue)) return -EINVAL; - /* Warn that the driver should choose an appropriate timestamp type */ - WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == - V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); - INIT_LIST_HEAD(&q->queued_list); INIT_LIST_HEAD(&q->done_list); spin_lock_init(&q->done_lock); @@ -2733,819 +2071,24 @@ int vb2_queue_init(struct vb2_queue *q) return 0; } -EXPORT_SYMBOL_GPL(vb2_queue_init); +EXPORT_SYMBOL_GPL(vb2_core_queue_init); /** - * vb2_queue_release() - stop streaming, release the queue and free memory + * vb2_core_queue_release() - stop streaming, release the queue and free memory * @q: videobuf2 queue * * This function stops streaming and performs necessary clean ups, including * freeing video buffer memory. The driver is responsible for freeing * the vb2_queue structure itself. */ -void vb2_queue_release(struct vb2_queue *q) +void vb2_core_queue_release(struct vb2_queue *q) { - __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); mutex_lock(&q->mmap_lock); __vb2_queue_free(q, q->num_buffers); mutex_unlock(&q->mmap_lock); } -EXPORT_SYMBOL_GPL(vb2_queue_release); - -/** - * struct vb2_fileio_buf - buffer context used by file io emulator - * - * vb2 provides a compatibility layer and emulator of file io (read and - * write) calls on top of streaming API. This structure is used for - * tracking context related to the buffers. - */ -struct vb2_fileio_buf { - void *vaddr; - unsigned int size; - unsigned int pos; - unsigned int queued:1; -}; - -/** - * struct vb2_fileio_data - queue context used by file io emulator - * - * @cur_index: the index of the buffer currently being read from or - * written to. If equal to q->num_buffers then a new buffer - * must be dequeued. - * @initial_index: in the read() case all buffers are queued up immediately - * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles - * buffers. However, in the write() case no buffers are initially - * queued, instead whenever a buffer is full it is queued up by - * __vb2_perform_fileio(). Only once all available buffers have - * been queued up will __vb2_perform_fileio() start to dequeue - * buffers. This means that initially __vb2_perform_fileio() - * needs to know what buffer index to use when it is queuing up - * the buffers for the first time. That initial index is stored - * in this field. Once it is equal to q->num_buffers all - * available buffers have been queued and __vb2_perform_fileio() - * should start the normal dequeue/queue cycle. - * - * vb2 provides a compatibility layer and emulator of file io (read and - * write) calls on top of streaming API. For proper operation it required - * this structure to save the driver state between each call of the read - * or write function. - */ -struct vb2_fileio_data { - struct v4l2_requestbuffers req; - struct v4l2_plane p; - struct v4l2_buffer b; - struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME]; - unsigned int cur_index; - unsigned int initial_index; - unsigned int q_count; - unsigned int dq_count; - unsigned read_once:1; - unsigned write_immediately:1; -}; - -/** - * __vb2_init_fileio() - initialize file io emulator - * @q: videobuf2 queue - * @read: mode selector (1 means read, 0 means write) - */ -static int __vb2_init_fileio(struct vb2_queue *q, int read) -{ - struct vb2_fileio_data *fileio; - int i, ret; - unsigned int count = 0; - - /* - * Sanity check - */ - if (WARN_ON((read && !(q->io_modes & VB2_READ)) || - (!read && !(q->io_modes & VB2_WRITE)))) - return -EINVAL; - - /* - * Check if device supports mapping buffers to kernel virtual space. - */ - if (!q->mem_ops->vaddr) - return -EBUSY; - - /* - * Check if streaming api has not been already activated. - */ - if (q->streaming || q->num_buffers > 0) - return -EBUSY; - - /* - * Start with count 1, driver can increase it in queue_setup() - */ - count = 1; - - dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", - (read) ? "read" : "write", count, q->fileio_read_once, - q->fileio_write_immediately); - - fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL); - if (fileio == NULL) - return -ENOMEM; - - fileio->read_once = q->fileio_read_once; - fileio->write_immediately = q->fileio_write_immediately; - - /* - * Request buffers and use MMAP type to force driver - * to allocate buffers by itself. - */ - fileio->req.count = count; - fileio->req.memory = V4L2_MEMORY_MMAP; - fileio->req.type = q->type; - q->fileio = fileio; - ret = __reqbufs(q, &fileio->req); - if (ret) - goto err_kfree; - - /* - * Check if plane_count is correct - * (multiplane buffers are not supported). - */ - if (q->bufs[0]->num_planes != 1) { - ret = -EBUSY; - goto err_reqbufs; - } - - /* - * Get kernel address of each buffer. - */ - for (i = 0; i < q->num_buffers; i++) { - fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0); - if (fileio->bufs[i].vaddr == NULL) { - ret = -EINVAL; - goto err_reqbufs; - } - fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0); - } - - /* - * Read mode requires pre queuing of all buffers. - */ - if (read) { - bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); - - /* - * Queue all buffers. - */ - for (i = 0; i < q->num_buffers; i++) { - struct v4l2_buffer *b = &fileio->b; - - memset(b, 0, sizeof(*b)); - b->type = q->type; - if (is_multiplanar) { - memset(&fileio->p, 0, sizeof(fileio->p)); - b->m.planes = &fileio->p; - b->length = 1; - } - b->memory = q->memory; - b->index = i; - ret = vb2_internal_qbuf(q, b); - if (ret) - goto err_reqbufs; - fileio->bufs[i].queued = 1; - } - /* - * All buffers have been queued, so mark that by setting - * initial_index to q->num_buffers - */ - fileio->initial_index = q->num_buffers; - fileio->cur_index = q->num_buffers; - } - - /* - * Start streaming. - */ - ret = vb2_internal_streamon(q, q->type); - if (ret) - goto err_reqbufs; - - return ret; - -err_reqbufs: - fileio->req.count = 0; - __reqbufs(q, &fileio->req); - -err_kfree: - q->fileio = NULL; - kfree(fileio); - return ret; -} - -/** - * __vb2_cleanup_fileio() - free resourced used by file io emulator - * @q: videobuf2 queue - */ -static int __vb2_cleanup_fileio(struct vb2_queue *q) -{ - struct vb2_fileio_data *fileio = q->fileio; - - if (fileio) { - vb2_internal_streamoff(q, q->type); - q->fileio = NULL; - fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); - kfree(fileio); - dprintk(3, "file io emulator closed\n"); - } - return 0; -} - -/** - * __vb2_perform_fileio() - perform a single file io (read or write) operation - * @q: videobuf2 queue - * @data: pointed to target userspace buffer - * @count: number of bytes to read or write - * @ppos: file handle position tracking pointer - * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) - * @read: access mode selector (1 means read, 0 means write) - */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) -{ - struct vb2_fileio_data *fileio; - struct vb2_fileio_buf *buf; - bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); - /* - * When using write() to write data to an output video node the vb2 core - * should set timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody - * else is able to provide this information with the write() operation. - */ - bool set_timestamp = !read && - (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == - V4L2_BUF_FLAG_TIMESTAMP_COPY; - int ret, index; - - dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", - read ? "read" : "write", (long)*ppos, count, - nonblock ? "non" : ""); - - if (!data) - return -EINVAL; - - /* - * Initialize emulator on first call. - */ - if (!vb2_fileio_is_active(q)) { - ret = __vb2_init_fileio(q, read); - dprintk(3, "vb2_init_fileio result: %d\n", ret); - if (ret) - return ret; - } - fileio = q->fileio; - - /* - * Check if we need to dequeue the buffer. - */ - index = fileio->cur_index; - if (index >= q->num_buffers) { - /* - * Call vb2_dqbuf to get buffer back. - */ - memset(&fileio->b, 0, sizeof(fileio->b)); - fileio->b.type = q->type; - fileio->b.memory = q->memory; - if (is_multiplanar) { - memset(&fileio->p, 0, sizeof(fileio->p)); - fileio->b.m.planes = &fileio->p; - fileio->b.length = 1; - } - ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); - dprintk(5, "vb2_dqbuf result: %d\n", ret); - if (ret) - return ret; - fileio->dq_count += 1; - - fileio->cur_index = index = fileio->b.index; - buf = &fileio->bufs[index]; - - /* - * Get number of bytes filled by the driver - */ - buf->pos = 0; - buf->queued = 0; - buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0) - : vb2_plane_size(q->bufs[index], 0); - /* Compensate for data_offset on read in the multiplanar case. */ - if (is_multiplanar && read && - fileio->b.m.planes[0].data_offset < buf->size) { - buf->pos = fileio->b.m.planes[0].data_offset; - buf->size -= buf->pos; - } - } else { - buf = &fileio->bufs[index]; - } - - /* - * Limit count on last few bytes of the buffer. - */ - if (buf->pos + count > buf->size) { - count = buf->size - buf->pos; - dprintk(5, "reducing read count: %zd\n", count); - } - - /* - * Transfer data to userspace. - */ - dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", - count, index, buf->pos); - if (read) - ret = copy_to_user(data, buf->vaddr + buf->pos, count); - else - ret = copy_from_user(buf->vaddr + buf->pos, data, count); - if (ret) { - dprintk(3, "error copying data\n"); - return -EFAULT; - } - - /* - * Update counters. - */ - buf->pos += count; - *ppos += count; - - /* - * Queue next buffer if required. - */ - if (buf->pos == buf->size || (!read && fileio->write_immediately)) { - /* - * Check if this is the last buffer to read. - */ - if (read && fileio->read_once && fileio->dq_count == 1) { - dprintk(3, "read limit reached\n"); - return __vb2_cleanup_fileio(q); - } - - /* - * Call vb2_qbuf and give buffer to the driver. - */ - memset(&fileio->b, 0, sizeof(fileio->b)); - fileio->b.type = q->type; - fileio->b.memory = q->memory; - fileio->b.index = index; - fileio->b.bytesused = buf->pos; - if (is_multiplanar) { - memset(&fileio->p, 0, sizeof(fileio->p)); - fileio->p.bytesused = buf->pos; - fileio->b.m.planes = &fileio->p; - fileio->b.length = 1; - } - if (set_timestamp) - v4l2_get_timestamp(&fileio->b.timestamp); - ret = vb2_internal_qbuf(q, &fileio->b); - dprintk(5, "vb2_dbuf result: %d\n", ret); - if (ret) - return ret; - - /* - * Buffer has been queued, update the status - */ - buf->pos = 0; - buf->queued = 1; - buf->size = vb2_plane_size(q->bufs[index], 0); - fileio->q_count += 1; - /* - * If we are queuing up buffers for the first time, then - * increase initial_index by one. - */ - if (fileio->initial_index < q->num_buffers) - fileio->initial_index++; - /* - * The next buffer to use is either a buffer that's going to be - * queued for the first time (initial_index < q->num_buffers) - * or it is equal to q->num_buffers, meaning that the next - * time we need to dequeue a buffer since we've now queued up - * all the 'first time' buffers. - */ - fileio->cur_index = fileio->initial_index; - } - - /* - * Return proper number of bytes processed. - */ - if (ret == 0) - ret = count; - return ret; -} - -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) -{ - return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); -} -EXPORT_SYMBOL_GPL(vb2_read); - -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblocking) -{ - return __vb2_perform_fileio(q, (char __user *) data, count, - ppos, nonblocking, 0); -} -EXPORT_SYMBOL_GPL(vb2_write); - -struct vb2_threadio_data { - struct task_struct *thread; - vb2_thread_fnc fnc; - void *priv; - bool stop; -}; - -static int vb2_thread(void *data) -{ - struct vb2_queue *q = data; - struct vb2_threadio_data *threadio = q->threadio; - struct vb2_fileio_data *fileio = q->fileio; - bool set_timestamp = false; - int prequeue = 0; - int index = 0; - int ret = 0; - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - prequeue = q->num_buffers; - set_timestamp = - (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == - V4L2_BUF_FLAG_TIMESTAMP_COPY; - } - - set_freezable(); - - for (;;) { - struct vb2_buffer *vb; - - /* - * Call vb2_dqbuf to get buffer back. - */ - memset(&fileio->b, 0, sizeof(fileio->b)); - fileio->b.type = q->type; - fileio->b.memory = q->memory; - if (prequeue) { - fileio->b.index = index++; - prequeue--; - } else { - call_void_qop(q, wait_finish, q); - if (!threadio->stop) - ret = vb2_internal_dqbuf(q, &fileio->b, 0); - call_void_qop(q, wait_prepare, q); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); - } - if (ret || threadio->stop) - break; - try_to_freeze(); - - vb = q->bufs[fileio->b.index]; - if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR)) - if (threadio->fnc(vb, threadio->priv)) - break; - call_void_qop(q, wait_finish, q); - if (set_timestamp) - v4l2_get_timestamp(&fileio->b.timestamp); - if (!threadio->stop) - ret = vb2_internal_qbuf(q, &fileio->b); - call_void_qop(q, wait_prepare, q); - if (ret || threadio->stop) - break; - } - - /* Hmm, linux becomes *very* unhappy without this ... */ - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - return 0; -} - -/* - * This function should not be used for anything else but the videobuf2-dvb - * support. If you think you have another good use-case for this, then please - * contact the linux-media mailinglist first. - */ -int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, - const char *thread_name) -{ - struct vb2_threadio_data *threadio; - int ret = 0; - - if (q->threadio) - return -EBUSY; - if (vb2_is_busy(q)) - return -EBUSY; - if (WARN_ON(q->fileio)) - return -EBUSY; - - threadio = kzalloc(sizeof(*threadio), GFP_KERNEL); - if (threadio == NULL) - return -ENOMEM; - threadio->fnc = fnc; - threadio->priv = priv; - - ret = __vb2_init_fileio(q, !V4L2_TYPE_IS_OUTPUT(q->type)); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); - if (ret) - goto nomem; - q->threadio = threadio; - threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name); - if (IS_ERR(threadio->thread)) { - ret = PTR_ERR(threadio->thread); - threadio->thread = NULL; - goto nothread; - } - return 0; - -nothread: - __vb2_cleanup_fileio(q); -nomem: - kfree(threadio); - return ret; -} -EXPORT_SYMBOL_GPL(vb2_thread_start); - -int vb2_thread_stop(struct vb2_queue *q) -{ - struct vb2_threadio_data *threadio = q->threadio; - int err; - - if (threadio == NULL) - return 0; - threadio->stop = true; - /* Wake up all pending sleeps in the thread */ - vb2_queue_error(q); - err = kthread_stop(threadio->thread); - __vb2_cleanup_fileio(q); - threadio->thread = NULL; - kfree(threadio); - q->threadio = NULL; - return err; -} -EXPORT_SYMBOL_GPL(vb2_thread_stop); - -/* - * The following functions are not part of the vb2 core API, but are helper - * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations - * and struct vb2_ops. - * They contain boilerplate code that most if not all drivers have to do - * and so they simplify the driver code. - */ - -/* The queue is busy if there is a owner and you are not that owner. */ -static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) -{ - return vdev->queue->owner && vdev->queue->owner != file->private_data; -} - -/* vb2 ioctl helpers */ - -int vb2_ioctl_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct video_device *vdev = video_devdata(file); - int res = __verify_memory_type(vdev->queue, p->memory, p->type); - - if (res) - return res; - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - res = __reqbufs(vdev->queue, p); - /* If count == 0, then the owner has released all buffers and he - is no longer owner of the queue. Otherwise we have a new owner. */ - if (res == 0) - vdev->queue->owner = p->count ? file->private_data : NULL; - return res; -} -EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); - -int vb2_ioctl_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *p) -{ - struct video_device *vdev = video_devdata(file); - int res = __verify_memory_type(vdev->queue, p->memory, p->format.type); - - p->index = vdev->queue->num_buffers; - /* If count == 0, then just check if memory and type are valid. - Any -EBUSY result from __verify_memory_type can be mapped to 0. */ - if (p->count == 0) - return res != -EBUSY ? res : 0; - if (res) - return res; - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - res = __create_bufs(vdev->queue, p); - if (res == 0) - vdev->queue->owner = file->private_data; - return res; -} -EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); - -int vb2_ioctl_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_prepare_buf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); - -int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ - return vb2_querybuf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); - -int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_qbuf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); - -int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); - -int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_streamon(vdev->queue, i); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); - -int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_streamoff(vdev->queue, i); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); - -int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_expbuf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); - -/* v4l2_file_operations helpers */ - -int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - - return vb2_mmap(vdev->queue, vma); -} -EXPORT_SYMBOL_GPL(vb2_fop_mmap); - -int _vb2_fop_release(struct file *file, struct mutex *lock) -{ - struct video_device *vdev = video_devdata(file); - - if (lock) - mutex_lock(lock); - if (file->private_data == vdev->queue->owner) { - vb2_queue_release(vdev->queue); - vdev->queue->owner = NULL; - } - if (lock) - mutex_unlock(lock); - return v4l2_fh_release(file); -} -EXPORT_SYMBOL_GPL(_vb2_fop_release); - -int vb2_fop_release(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - - return _vb2_fop_release(file, lock); -} -EXPORT_SYMBOL_GPL(vb2_fop_release); - -ssize_t vb2_fop_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int err = -EBUSY; - - if (!(vdev->queue->io_modes & VB2_WRITE)) - return -EINVAL; - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - if (vb2_queue_is_busy(vdev, file)) - goto exit; - err = vb2_write(vdev->queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); - if (vdev->queue->fileio) - vdev->queue->owner = file->private_data; -exit: - if (lock) - mutex_unlock(lock); - return err; -} -EXPORT_SYMBOL_GPL(vb2_fop_write); - -ssize_t vb2_fop_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int err = -EBUSY; - - if (!(vdev->queue->io_modes & VB2_READ)) - return -EINVAL; - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - if (vb2_queue_is_busy(vdev, file)) - goto exit; - err = vb2_read(vdev->queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); - if (vdev->queue->fileio) - vdev->queue->owner = file->private_data; -exit: - if (lock) - mutex_unlock(lock); - return err; -} -EXPORT_SYMBOL_GPL(vb2_fop_read); - -unsigned int vb2_fop_poll(struct file *file, poll_table *wait) -{ - struct video_device *vdev = video_devdata(file); - struct vb2_queue *q = vdev->queue; - struct mutex *lock = q->lock ? q->lock : vdev->lock; - unsigned res; - void *fileio; - - /* - * If this helper doesn't know how to lock, then you shouldn't be using - * it but you should write your own. - */ - WARN_ON(!lock); - - if (lock && mutex_lock_interruptible(lock)) - return POLLERR; - - fileio = q->fileio; - - res = vb2_poll(vdev->queue, file, wait); - - /* If fileio was started, then we have a new queue owner. */ - if (!fileio && q->fileio) - q->owner = file->private_data; - if (lock) - mutex_unlock(lock); - return res; -} -EXPORT_SYMBOL_GPL(vb2_fop_poll); - -#ifndef CONFIG_MMU -unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, - unsigned long len, unsigned long pgoff, unsigned long flags) -{ - struct video_device *vdev = video_devdata(file); - - return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); -} -EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); -#endif - -/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ - -void vb2_ops_wait_prepare(struct vb2_queue *vq) -{ - mutex_unlock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); - -void vb2_ops_wait_finish(struct vb2_queue *vq) -{ - mutex_lock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); +EXPORT_SYMBOL_GPL(vb2_core_queue_release); MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); diff --git a/kernel/drivers/media/v4l2-core/videobuf2-dma-contig.c b/kernel/drivers/media/v4l2-core/videobuf2-dma-contig.c index 644dec73d..c33127284 100644 --- a/kernel/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/kernel/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct frame_vector *vec; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_t refcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /* scatterlist table functions */ /*********************************************/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -120,7 +100,8 @@ static void vb2_dc_prepare(void *buf_priv) if (!sgt || buf->db_attach) return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, + buf->dma_dir); } static void vb2_dc_finish(void *buf_priv) @@ -132,7 +113,7 @@ static void vb2_dc_finish(void *buf_priv) if (!sgt || buf->db_attach) return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } /*********************************************/ @@ -299,7 +280,6 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( /* stealing dmabuf mutex to serialize map/unmap operations */ struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - int ret; mutex_lock(lock); @@ -318,8 +298,9 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( } /* mapping to the client with new direction */ - ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir); - if (ret <= 0) { + sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, + dma_dir); + if (!sgt->nents) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); @@ -429,92 +410,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*********************************************/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { DEFINE_DMA_ATTRS(attrs); @@ -526,13 +427,15 @@ static void vb2_dc_put_userptr(void *buf_priv) */ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir, &attrs); - if (!vma_is_io(buf->vma)) - vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page); - + pages = frame_vector_pages(buf->vec); + /* sgt should exist only if vector contains pages... */ + BUG_ON(IS_ERR(pages)); + for (i = 0; i < frame_vector_count(buf->vec); i++) + set_page_dirty_lock(pages[i]); sg_free_table(sgt); kfree(sgt); } - vb2_put_vma(buf->vma); + vb2_destroy_framevec(buf->vec); kfree(buf); } @@ -572,13 +475,10 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, { struct vb2_dc_conf *conf = alloc_ctx; struct vb2_dc_buf *buf; - unsigned long start; - unsigned long end; + struct frame_vector *vec; unsigned long offset; - struct page **pages; - int n_pages; + int n_pages, i; int ret = 0; - struct vm_area_struct *vma; struct sg_table *sgt; unsigned long contig_size; unsigned long dma_align = dma_get_cache_alignment(); @@ -604,72 +504,43 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dev = conf->dev; buf->dma_dir = dma_dir; - start = vaddr & PAGE_MASK; offset = vaddr & ~PAGE_MASK; - end = PAGE_ALIGN(vaddr + size); - n_pages = (end - start) >> PAGE_SHIFT; - - pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL); - if (!pages) { - ret = -ENOMEM; - pr_err("failed to allocate pages table\n"); + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) { + ret = PTR_ERR(vec); goto fail_buf; } + buf->vec = vec; + n_pages = frame_vector_count(vec); + ret = frame_vector_to_pages(vec); + if (ret < 0) { + unsigned long *nums = frame_vector_pfns(vec); - /* current->mm->mmap_sem is taken by videobuf2 core */ - vma = find_vma(current->mm, vaddr); - if (!vma) { - pr_err("no vma for address %lu\n", vaddr); - ret = -EFAULT; - goto fail_pages; - } - - if (vma->vm_end < vaddr + size) { - pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size); - ret = -EFAULT; - goto fail_pages; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - pr_err("failed to copy vma\n"); - ret = -ENOMEM; - goto fail_pages; - } - - /* extract page list from userspace mapping */ - ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, dma_dir); - if (ret) { - unsigned long pfn; - if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { - buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); - buf->size = size; - kfree(pages); - return buf; - } - - pr_err("failed to get user pages\n"); - goto fail_vma; + /* + * Failed to convert to pages... Check the memory is physically + * contiguous and use direct mapping + */ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_pfnvec; + buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]); + goto out; } sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { pr_err("failed to allocate sg table\n"); ret = -ENOMEM; - goto fail_get_user_pages; + goto fail_pfnvec; } - ret = sg_alloc_table_from_pages(sgt, pages, n_pages, + ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages, offset, size, GFP_KERNEL); if (ret) { pr_err("failed to initialize sg table\n"); goto fail_sgt; } - /* pages are no longer needed */ - kfree(pages); - pages = NULL; - /* * No need to sync to the device, this will happen later when the * prepare() memop is called. @@ -691,8 +562,9 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, } buf->dma_addr = sg_dma_address(sgt->sgl); - buf->size = size; buf->dma_sgt = sgt; +out: + buf->size = size; return buf; @@ -701,23 +573,13 @@ fail_map_sg: buf->dma_dir, &attrs); fail_sgt_init: - if (!vma_is_io(buf->vma)) - vb2_dc_sgt_foreach_page(sgt, put_page); sg_free_table(sgt); fail_sgt: kfree(sgt); -fail_get_user_pages: - if (pages && !vma_is_io(buf->vma)) - while (n_pages) - put_page(pages[--n_pages]); - -fail_vma: - vb2_put_vma(buf->vma); - -fail_pages: - kfree(pages); /* kfree is NULL-proof */ +fail_pfnvec: + vb2_destroy_framevec(vec); fail_buf: kfree(buf); diff --git a/kernel/drivers/media/v4l2-core/videobuf2-dma-sg.c b/kernel/drivers/media/v4l2-core/videobuf2-dma-sg.c index 45c708e46..9985c89f0 100644 --- a/kernel/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/kernel/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void *vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned int num_pages; atomic_t refcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -147,8 +147,9 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, * No need to sync to the device, this will happen later when the * prepare() memop is called. */ - if (dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->nents, - buf->dma_dir, &attrs) == 0) + sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, + buf->dma_dir, &attrs); + if (!sgt->nents) goto fail_map; buf->handler.refcount = &buf->refcount; @@ -187,7 +188,7 @@ static void vb2_dma_sg_put(void *buf_priv) dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); dprintk(1, "%s: Freeing buffer of %d pages\n", __func__, buf->num_pages); - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->nents, + dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir, &attrs); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->num_pages); @@ -209,7 +210,8 @@ static void vb2_dma_sg_prepare(void *buf_priv) if (buf->db_attach) return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, + buf->dma_dir); } static void vb2_dma_sg_finish(void *buf_priv) @@ -221,12 +223,7 @@ static void vb2_dma_sg_finish(void *buf_priv) if (buf->db_attach) return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); -} - -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, @@ -235,14 +232,11 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -253,85 +247,37 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; - num_pages_from_user < buf->num_pages; - ++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, - buf->num_pages, - buf->dma_dir == DMA_FROM_DEVICE, - 1, /* force */ - buf->pages, - NULL); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_pages, buf->offset, size, 0)) - goto userptr_fail_alloc_table_from_pages; + goto userptr_fail_sgtable; sgt = &buf->sg_table; /* * No need to sync to the device, this will happen later when the * prepare() memop is called. */ - if (dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->nents, - buf->dma_dir, &attrs) == 0) + sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, + buf->dma_dir, &attrs); + if (!sgt->nents) goto userptr_fail_map; + return buf; userptr_fail_map: sg_free_table(&buf->sg_table); -userptr_fail_alloc_table_from_pages: -userptr_fail_get_user_pages: - dprintk(1, "get_user_pages requested/got: %d/%d]\n", - buf->num_pages, num_pages_from_user); - if (!vma_is_io(buf->vma)) - while (--num_pages_from_user >= 0) - put_page(buf->pages[num_pages_from_user]); - vb2_put_vma(buf->vma); -userptr_fail_find_vma: - kfree(buf->pages); -userptr_fail_alloc_pages: +userptr_fail_sgtable: + vb2_destroy_framevec(vec); +userptr_fail_pfnvec: kfree(buf); return NULL; } @@ -351,18 +297,16 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) dprintk(1, "%s: Releasing userspace buffer of %d pages\n", __func__, buf->num_pages); - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir, &attrs); + dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir, + &attrs); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->num_pages); sg_free_table(buf->dma_sgt); while (--i >= 0) { if (buf->dma_dir == DMA_FROM_DEVICE) set_page_dirty_lock(buf->pages[i]); - if (!vma_is_io(buf->vma)) - put_page(buf->pages[i]); } - kfree(buf->pages); - vb2_put_vma(buf->vma); + vb2_destroy_framevec(buf->vec); kfree(buf); } @@ -502,7 +446,6 @@ static struct sg_table *vb2_dma_sg_dmabuf_ops_map( /* stealing dmabuf mutex to serialize map/unmap operations */ struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - int ret; mutex_lock(lock); @@ -521,8 +464,9 @@ static struct sg_table *vb2_dma_sg_dmabuf_ops_map( } /* mapping to the client with new direction */ - ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir); - if (ret <= 0) { + sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, + dma_dir); + if (!sgt->nents) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); diff --git a/kernel/drivers/media/v4l2-core/videobuf2-internal.h b/kernel/drivers/media/v4l2-core/videobuf2-internal.h new file mode 100644 index 000000000..79018c749 --- /dev/null +++ b/kernel/drivers/media/v4l2-core/videobuf2-internal.h @@ -0,0 +1,161 @@ +#ifndef _MEDIA_VIDEOBUF2_INTERNAL_H +#define _MEDIA_VIDEOBUF2_INTERNAL_H + +#include +#include +#include +#include + +extern int vb2_debug; + +#define dprintk(level, fmt, arg...) \ + do { \ + if (vb2_debug >= level) \ + pr_info("vb2: %s: " fmt, __func__, ## arg); \ + } while (0) + +#ifdef CONFIG_VIDEO_ADV_DEBUG + +/* + * If advanced debugging is on, then count how often each op is called + * successfully, which can either be per-buffer or per-queue. + * + * This makes it easy to check that the 'init' and 'cleanup' + * (and variations thereof) stay balanced. + */ + +#define log_memop(vb, op) \ + dprintk(2, "call_memop(%p, %d, %s)%s\n", \ + (vb)->vb2_queue, (vb)->index, #op, \ + (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") + +#define call_memop(vb, op, args...) \ +({ \ + struct vb2_queue *_q = (vb)->vb2_queue; \ + int err; \ + \ + log_memop(vb, op); \ + err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \ + if (!err) \ + (vb)->cnt_mem_ ## op++; \ + err; \ +}) + +#define call_ptr_memop(vb, op, args...) \ +({ \ + struct vb2_queue *_q = (vb)->vb2_queue; \ + void *ptr; \ + \ + log_memop(vb, op); \ + ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL; \ + if (!IS_ERR_OR_NULL(ptr)) \ + (vb)->cnt_mem_ ## op++; \ + ptr; \ +}) + +#define call_void_memop(vb, op, args...) \ +({ \ + struct vb2_queue *_q = (vb)->vb2_queue; \ + \ + log_memop(vb, op); \ + if (_q->mem_ops->op) \ + _q->mem_ops->op(args); \ + (vb)->cnt_mem_ ## op++; \ +}) + +#define log_qop(q, op) \ + dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ + (q)->ops->op ? "" : " (nop)") + +#define call_qop(q, op, args...) \ +({ \ + int err; \ + \ + log_qop(q, op); \ + err = (q)->ops->op ? (q)->ops->op(args) : 0; \ + if (!err) \ + (q)->cnt_ ## op++; \ + err; \ +}) + +#define call_void_qop(q, op, args...) \ +({ \ + log_qop(q, op); \ + if ((q)->ops->op) \ + (q)->ops->op(args); \ + (q)->cnt_ ## op++; \ +}) + +#define log_vb_qop(vb, op, args...) \ + dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ + (vb)->vb2_queue, (vb)->index, #op, \ + (vb)->vb2_queue->ops->op ? "" : " (nop)") + +#define call_vb_qop(vb, op, args...) \ +({ \ + int err; \ + \ + log_vb_qop(vb, op); \ + err = (vb)->vb2_queue->ops->op ? \ + (vb)->vb2_queue->ops->op(args) : 0; \ + if (!err) \ + (vb)->cnt_ ## op++; \ + err; \ +}) + +#define call_void_vb_qop(vb, op, args...) \ +({ \ + log_vb_qop(vb, op); \ + if ((vb)->vb2_queue->ops->op) \ + (vb)->vb2_queue->ops->op(args); \ + (vb)->cnt_ ## op++; \ +}) + +#else + +#define call_memop(vb, op, args...) \ + ((vb)->vb2_queue->mem_ops->op ? \ + (vb)->vb2_queue->mem_ops->op(args) : 0) + +#define call_ptr_memop(vb, op, args...) \ + ((vb)->vb2_queue->mem_ops->op ? \ + (vb)->vb2_queue->mem_ops->op(args) : NULL) + +#define call_void_memop(vb, op, args...) \ + do { \ + if ((vb)->vb2_queue->mem_ops->op) \ + (vb)->vb2_queue->mem_ops->op(args); \ + } while (0) + +#define call_qop(q, op, args...) \ + ((q)->ops->op ? (q)->ops->op(args) : 0) + +#define call_void_qop(q, op, args...) \ + do { \ + if ((q)->ops->op) \ + (q)->ops->op(args); \ + } while (0) + +#define call_vb_qop(vb, op, args...) \ + ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0) + +#define call_void_vb_qop(vb, op, args...) \ + do { \ + if ((vb)->vb2_queue->ops->op) \ + (vb)->vb2_queue->ops->op(args); \ + } while (0) + +#endif + +#define call_bufop(q, op, args...) \ +({ \ + int ret = 0; \ + if (q && q->buf_ops && q->buf_ops->op) \ + ret = q->buf_ops->op(args); \ + ret; \ +}) + +bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb); +int vb2_verify_memory_type(struct vb2_queue *q, + enum vb2_memory memory, unsigned int type); +#endif /* _MEDIA_VIDEOBUF2_INTERNAL_H */ diff --git a/kernel/drivers/media/v4l2-core/videobuf2-memops.c b/kernel/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2..dbec5923f 100644 --- a/kernel/drivers/media/v4l2-core/videobuf2-memops.c +++ b/kernel/drivers/media/v4l2-core/videobuf2-memops.c @@ -19,122 +19,66 @@ #include #include -#include +#include #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length: Length of a range to map + * @write: Should we map for writing into the area * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). */ -void vb2_put_vma(struct vm_area_struct *vma) +struct frame_vector *vb2_create_framevec(unsigned long start, + unsigned long length, + bool write) { - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(vb2_put_vma); +EXPORT_SYMBOL(vb2_create_framevec); /** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release * - * Returns 0 on success. + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) +void vb2_destroy_framevec(struct frame_vector *vec) { - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* - * Memory is contigous, lock vma and return to the caller - */ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; + put_vaddr_frames(vec); + frame_vector_destroy(vec); } -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); +EXPORT_SYMBOL(vb2_destroy_framevec); /** * vb2_common_vm_open() - increase refcount of the vma diff --git a/kernel/drivers/media/v4l2-core/videobuf2-v4l2.c b/kernel/drivers/media/v4l2-core/videobuf2-v4l2.c new file mode 100644 index 000000000..502984c72 --- /dev/null +++ b/kernel/drivers/media/v4l2-core/videobuf2-v4l2.c @@ -0,0 +1,1661 @@ +/* + * videobuf2-v4l2.c - V4L2 driver helper framework + * + * Copyright (C) 2010 Samsung Electronics + * + * Author: Pawel Osciak + * Marek Szyprowski + * + * The vb2_thread implementation was based on code from videobuf-dvb.c: + * (c) 2004 Gerd Knorr [SUSE Labs] + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "videobuf2-internal.h" + +/* Flags that are set by the vb2 core */ +#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ + V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ + V4L2_BUF_FLAG_PREPARED | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) +/* Output buffer flags that should be passed on to the driver */ +#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \ + V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE) + +/** + * __verify_planes_array() - verify that the planes array passed in struct + * v4l2_buffer from userspace can be safely used + */ +static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + if (!V4L2_TYPE_IS_MULTIPLANAR(b->type)) + return 0; + + /* Is memory for copying plane information present? */ + if (NULL == b->m.planes) { + dprintk(1, "multi-planar buffer passed but " + "planes array not provided\n"); + return -EINVAL; + } + + if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { + dprintk(1, "incorrect planes array length, " + "expected %d, got %d\n", vb->num_planes, b->length); + return -EINVAL; + } + + return 0; +} + +/** + * __verify_length() - Verify that the bytesused value for each plane fits in + * the plane length and that the data offset doesn't exceed the bytesused value. + */ +static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + unsigned int length; + unsigned int bytesused; + unsigned int plane; + + if (!V4L2_TYPE_IS_OUTPUT(b->type)) + return 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + for (plane = 0; plane < vb->num_planes; ++plane) { + length = (b->memory == VB2_MEMORY_USERPTR || + b->memory == VB2_MEMORY_DMABUF) + ? b->m.planes[plane].length + : vb->planes[plane].length; + bytesused = b->m.planes[plane].bytesused + ? b->m.planes[plane].bytesused : length; + + if (b->m.planes[plane].bytesused > length) + return -EINVAL; + + if (b->m.planes[plane].data_offset > 0 && + b->m.planes[plane].data_offset >= bytesused) + return -EINVAL; + } + } else { + length = (b->memory == VB2_MEMORY_USERPTR) + ? b->length : vb->planes[0].length; + + if (b->bytesused > length) + return -EINVAL; + } + + return 0; +} + +static int __set_timestamp(struct vb2_buffer *vb, const void *pb) +{ + const struct v4l2_buffer *b = pb; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *q = vb->vb2_queue; + + if (q->is_output) { + /* + * For output buffers copy the timestamp if needed, + * and the timecode field and flag if needed. + */ + if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY) + vbuf->timestamp = b->timestamp; + vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; + if (b->flags & V4L2_BUF_FLAG_TIMECODE) + vbuf->timecode = b->timecode; + } + return 0; +}; + +static void vb2_warn_zero_bytesused(struct vb2_buffer *vb) +{ + static bool check_once; + + if (check_once) + return; + + check_once = true; + WARN_ON(1); + + pr_warn("use of bytesused == 0 is deprecated and will be removed in the future,\n"); + if (vb->vb2_queue->allow_zero_bytesused) + pr_warn("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n"); + else + pr_warn("use the actual size instead.\n"); +} + +static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, + const char *opname) +{ + if (b->type != q->type) { + dprintk(1, "%s: invalid buffer type\n", opname); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "%s: buffer index out of range\n", opname); + return -EINVAL; + } + + if (q->bufs[b->index] == NULL) { + /* Should never happen */ + dprintk(1, "%s: buffer is NULL\n", opname); + return -EINVAL; + } + + if (b->memory != q->memory) { + dprintk(1, "%s: invalid memory type\n", opname); + return -EINVAL; + } + + return __verify_planes_array(q->bufs[b->index], b); +} + +/** + * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be + * returned to userspace + */ +static int __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb) +{ + struct v4l2_buffer *b = pb; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *q = vb->vb2_queue; + unsigned int plane; + + /* Copy back data such as timestamp, flags, etc. */ + b->index = vb->index; + b->type = vb->type; + b->memory = vb->memory; + b->bytesused = 0; + + b->flags = vbuf->flags; + b->field = vbuf->field; + b->timestamp = vbuf->timestamp; + b->timecode = vbuf->timecode; + b->sequence = vbuf->sequence; + b->reserved2 = 0; + b->reserved = 0; + + if (q->is_multiplanar) { + /* + * Fill in plane-related data if userspace provided an array + * for it. The caller has already verified memory and size. + */ + b->length = vb->num_planes; + for (plane = 0; plane < vb->num_planes; ++plane) { + struct v4l2_plane *pdst = &b->m.planes[plane]; + struct vb2_plane *psrc = &vb->planes[plane]; + + pdst->bytesused = psrc->bytesused; + pdst->length = psrc->length; + if (q->memory == VB2_MEMORY_MMAP) + pdst->m.mem_offset = psrc->m.offset; + else if (q->memory == VB2_MEMORY_USERPTR) + pdst->m.userptr = psrc->m.userptr; + else if (q->memory == VB2_MEMORY_DMABUF) + pdst->m.fd = psrc->m.fd; + pdst->data_offset = psrc->data_offset; + memset(pdst->reserved, 0, sizeof(pdst->reserved)); + } + } else { + /* + * We use length and offset in v4l2_planes array even for + * single-planar buffers, but userspace does not. + */ + b->length = vb->planes[0].length; + b->bytesused = vb->planes[0].bytesused; + if (q->memory == VB2_MEMORY_MMAP) + b->m.offset = vb->planes[0].m.offset; + else if (q->memory == VB2_MEMORY_USERPTR) + b->m.userptr = vb->planes[0].m.userptr; + else if (q->memory == VB2_MEMORY_DMABUF) + b->m.fd = vb->planes[0].m.fd; + } + + /* + * Clear any buffer state related flags. + */ + b->flags &= ~V4L2_BUFFER_MASK_FLAGS; + b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; + if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != + V4L2_BUF_FLAG_TIMESTAMP_COPY) { + /* + * For non-COPY timestamps, drop timestamp source bits + * and obtain the timestamp source from the queue. + */ + b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + } + + switch (vb->state) { + case VB2_BUF_STATE_QUEUED: + case VB2_BUF_STATE_ACTIVE: + b->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case VB2_BUF_STATE_ERROR: + b->flags |= V4L2_BUF_FLAG_ERROR; + /* fall through */ + case VB2_BUF_STATE_DONE: + b->flags |= V4L2_BUF_FLAG_DONE; + break; + case VB2_BUF_STATE_PREPARED: + b->flags |= V4L2_BUF_FLAG_PREPARED; + break; + case VB2_BUF_STATE_PREPARING: + case VB2_BUF_STATE_DEQUEUED: + case VB2_BUF_STATE_REQUEUEING: + /* nothing */ + break; + } + + if (vb2_buffer_in_use(q, vb)) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + return 0; +} + +/** + * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a + * v4l2_buffer by the userspace. It also verifies that struct + * v4l2_buffer has a valid number of planes. + */ +static int __fill_vb2_buffer(struct vb2_buffer *vb, + const void *pb, struct vb2_plane *planes) +{ + struct vb2_queue *q = vb->vb2_queue; + const struct v4l2_buffer *b = pb; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + unsigned int plane; + int ret; + + ret = __verify_length(vb, b); + if (ret < 0) { + dprintk(1, "plane parameters verification failed: %d\n", ret); + return ret; + } + if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) { + /* + * If the format's field is ALTERNATE, then the buffer's field + * should be either TOP or BOTTOM, not ALTERNATE since that + * makes no sense. The driver has to know whether the + * buffer represents a top or a bottom field in order to + * program any DMA correctly. Using ALTERNATE is wrong, since + * that just says that it is either a top or a bottom field, + * but not which of the two it is. + */ + dprintk(1, "the field is incorrectly set to ALTERNATE " + "for an output buffer\n"); + return -EINVAL; + } + vbuf->timestamp.tv_sec = 0; + vbuf->timestamp.tv_usec = 0; + vbuf->sequence = 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + if (b->memory == VB2_MEMORY_USERPTR) { + for (plane = 0; plane < vb->num_planes; ++plane) { + planes[plane].m.userptr = + b->m.planes[plane].m.userptr; + planes[plane].length = + b->m.planes[plane].length; + } + } + if (b->memory == VB2_MEMORY_DMABUF) { + for (plane = 0; plane < vb->num_planes; ++plane) { + planes[plane].m.fd = + b->m.planes[plane].m.fd; + planes[plane].length = + b->m.planes[plane].length; + } + } + + /* Fill in driver-provided information for OUTPUT types */ + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * Will have to go up to b->length when API starts + * accepting variable number of planes. + * + * If bytesused == 0 for the output buffer, then fall + * back to the full buffer size. In that case + * userspace clearly never bothered to set it and + * it's a safe assumption that they really meant to + * use the full plane sizes. + * + * Some drivers, e.g. old codec drivers, use bytesused == 0 + * as a way to indicate that streaming is finished. + * In that case, the driver should use the + * allow_zero_bytesused flag to keep old userspace + * applications working. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + struct vb2_plane *pdst = &planes[plane]; + struct v4l2_plane *psrc = &b->m.planes[plane]; + + if (psrc->bytesused == 0) + vb2_warn_zero_bytesused(vb); + + if (vb->vb2_queue->allow_zero_bytesused) + pdst->bytesused = psrc->bytesused; + else + pdst->bytesused = psrc->bytesused ? + psrc->bytesused : pdst->length; + pdst->data_offset = psrc->data_offset; + } + } + } else { + /* + * Single-planar buffers do not use planes array, + * so fill in relevant v4l2_buffer struct fields instead. + * In videobuf we use our internal V4l2_planes struct for + * single-planar buffers as well, for simplicity. + * + * If bytesused == 0 for the output buffer, then fall back + * to the full buffer size as that's a sensible default. + * + * Some drivers, e.g. old codec drivers, use bytesused == 0 as + * a way to indicate that streaming is finished. In that case, + * the driver should use the allow_zero_bytesused flag to keep + * old userspace applications working. + */ + if (b->memory == VB2_MEMORY_USERPTR) { + planes[0].m.userptr = b->m.userptr; + planes[0].length = b->length; + } + + if (b->memory == VB2_MEMORY_DMABUF) { + planes[0].m.fd = b->m.fd; + planes[0].length = b->length; + } + + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + if (b->bytesused == 0) + vb2_warn_zero_bytesused(vb); + + if (vb->vb2_queue->allow_zero_bytesused) + planes[0].bytesused = b->bytesused; + else + planes[0].bytesused = b->bytesused ? + b->bytesused : planes[0].length; + } else + planes[0].bytesused = 0; + + } + + /* Zero flags that the vb2 core handles */ + vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; + if ((vb->vb2_queue->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != + V4L2_BUF_FLAG_TIMESTAMP_COPY || !V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * Non-COPY timestamps and non-OUTPUT queues will get + * their timestamp and timestamp source flags from the + * queue. + */ + vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + } + + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * For output buffers mask out the timecode flag: + * this will be handled later in vb2_internal_qbuf(). + * The 'field' is valid metadata for this output buffer + * and so that needs to be copied here. + */ + vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE; + vbuf->field = b->field; + } else { + /* Zero any output buffer flags as this is a capture buffer */ + vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS; + } + + return 0; +} + +static const struct vb2_buf_ops v4l2_buf_ops = { + .fill_user_buffer = __fill_v4l2_buffer, + .fill_vb2_buffer = __fill_vb2_buffer, + .set_timestamp = __set_timestamp, +}; + +/** + * vb2_querybuf() - query video buffer information + * @q: videobuf queue + * @b: buffer struct passed from userspace to vidioc_querybuf handler + * in driver + * + * Should be called from vidioc_querybuf ioctl handler in driver. + * This function will verify the passed v4l2_buffer structure and fill the + * relevant information for the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_querybuf handler in driver. + */ +int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + struct vb2_buffer *vb; + int ret; + + if (b->type != q->type) { + dprintk(1, "wrong buffer type\n"); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "buffer index out of range\n"); + return -EINVAL; + } + vb = q->bufs[b->index]; + ret = __verify_planes_array(vb, b); + + return ret ? ret : vb2_core_querybuf(q, b->index, b); +} +EXPORT_SYMBOL(vb2_querybuf); + +/** + * vb2_reqbufs() - Wrapper for vb2_core_reqbufs() that also verifies + * the memory and type values. + * @q: videobuf2 queue + * @req: struct passed from userspace to vidioc_reqbufs handler + * in driver + */ +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) +{ + int ret = vb2_verify_memory_type(q, req->memory, req->type); + + return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); +} +EXPORT_SYMBOL_GPL(vb2_reqbufs); + +/** + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_prepare_buf + * handler in driver + * + * Should be called from vidioc_prepare_buf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, + * + * The return values from this function are intended to be directly returned + * from vidioc_prepare_buf handler in driver. + */ +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + int ret; + + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); + return -EBUSY; + } + + ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf"); + + return ret ? ret : vb2_core_prepare_buf(q, b->index, b); +} +EXPORT_SYMBOL_GPL(vb2_prepare_buf); + +/** + * vb2_create_bufs() - Wrapper for vb2_core_create_bufs() that also verifies + * the memory and type values. + * @q: videobuf2 queue + * @create: creation parameters, passed from userspace to vidioc_create_bufs + * handler in driver + */ +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +{ + int ret = vb2_verify_memory_type(q, create->memory, + create->format.type); + + create->index = q->num_buffers; + if (create->count == 0) + return ret != -EBUSY ? ret : 0; + return ret ? ret : vb2_core_create_bufs(q, create->memory, + &create->count, &create->format); +} +EXPORT_SYMBOL_GPL(vb2_create_bufs); + +static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); + + return ret ? ret : vb2_core_qbuf(q, b->index, b); +} + +/** + * vb2_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); + return -EBUSY; + } + + return vb2_internal_qbuf(q, b); +} +EXPORT_SYMBOL_GPL(vb2_qbuf); + +static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, + bool nonblocking) +{ + int ret; + + if (b->type != q->type) { + dprintk(1, "invalid buffer type\n"); + return -EINVAL; + } + + ret = vb2_core_dqbuf(q, b, nonblocking); + + if (!ret && !q->is_output && + b->flags & V4L2_BUF_FLAG_LAST) + q->last_buffer_dequeued = true; + + return ret; +} + +/** + * vb2_dqbuf() - Dequeue a buffer to the userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_dqbuf handler + * in driver + * @nonblocking: if true, this call will not sleep waiting for a buffer if no + * buffers ready for dequeuing are present. Normally the driver + * would be passing (file->f_flags & O_NONBLOCK) here + * + * Should be called from vidioc_dqbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_finish callback in the driver (if provided), in which + * driver can perform any additional operations that may be required before + * returning the buffer to userspace, such as cache sync, + * 3) the buffer struct members are filled with relevant information for + * the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_dqbuf handler in driver. + */ +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) +{ + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); + return -EBUSY; + } + return vb2_internal_dqbuf(q, b, nonblocking); +} +EXPORT_SYMBOL_GPL(vb2_dqbuf); + +/** + * vb2_streamon - start streaming + * @q: videobuf2 queue + * @type: type argument passed from userspace to vidioc_streamon handler + * + * Should be called from vidioc_streamon handler of a driver. + * This function: + * 1) verifies current state + * 2) passes any previously queued buffers to the driver and starts streaming + * + * The return values from this function are intended to be directly returned + * from vidioc_streamon handler in the driver. + */ +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) +{ + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); + return -EBUSY; + } + return vb2_core_streamon(q, type); +} +EXPORT_SYMBOL_GPL(vb2_streamon); + +/** + * vb2_streamoff - stop streaming + * @q: videobuf2 queue + * @type: type argument passed from userspace to vidioc_streamoff handler + * + * Should be called from vidioc_streamoff handler of a driver. + * This function: + * 1) verifies current state, + * 2) stop streaming and dequeues any queued buffers, including those previously + * passed to the driver (after waiting for the driver to finish). + * + * This call can be used for pausing playback. + * The return values from this function are intended to be directly returned + * from vidioc_streamoff handler in the driver + */ +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) +{ + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); + return -EBUSY; + } + return vb2_core_streamoff(q, type); +} +EXPORT_SYMBOL_GPL(vb2_streamoff); + +/** + * vb2_expbuf() - Export a buffer as a file descriptor + * @q: videobuf2 queue + * @eb: export buffer structure passed from userspace to vidioc_expbuf + * handler in driver + * + * The return values from this function are intended to be directly returned + * from vidioc_expbuf handler in driver. + */ +int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) +{ + return vb2_core_expbuf(q, &eb->fd, eb->type, eb->index, + eb->plane, eb->flags); +} +EXPORT_SYMBOL_GPL(vb2_expbuf); + +/** + * vb2_queue_init() - initialize a videobuf2 queue + * @q: videobuf2 queue; this structure should be allocated in driver + * + * The vb2_queue structure should be allocated by the driver. The driver is + * responsible of clearing it's content and setting initial values for some + * required entries before calling this function. + * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer + * to the struct vb2_queue description in include/media/videobuf2-core.h + * for more information. + */ +int vb2_queue_init(struct vb2_queue *q) +{ + /* + * Sanity check + */ + if (WARN_ON(!q) || + WARN_ON(q->timestamp_flags & + ~(V4L2_BUF_FLAG_TIMESTAMP_MASK | + V4L2_BUF_FLAG_TSTAMP_SRC_MASK))) + return -EINVAL; + + /* Warn that the driver should choose an appropriate timestamp type */ + WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); + + /* Warn that vb2_memory should match with v4l2_memory */ + if (WARN_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP) + || WARN_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR) + || WARN_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF)) + return -EINVAL; + + if (q->buf_struct_size == 0) + q->buf_struct_size = sizeof(struct vb2_v4l2_buffer); + + q->buf_ops = &v4l2_buf_ops; + q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); + q->is_output = V4L2_TYPE_IS_OUTPUT(q->type); + + return vb2_core_queue_init(q); +} +EXPORT_SYMBOL_GPL(vb2_queue_init); + +static int __vb2_init_fileio(struct vb2_queue *q, int read); +static int __vb2_cleanup_fileio(struct vb2_queue *q); + +/** + * vb2_queue_release() - stop streaming, release the queue and free memory + * @q: videobuf2 queue + * + * This function stops streaming and performs necessary clean ups, including + * freeing video buffer memory. The driver is responsible for freeing + * the vb2_queue structure itself. + */ +void vb2_queue_release(struct vb2_queue *q) +{ + __vb2_cleanup_fileio(q); + vb2_core_queue_release(q); +} +EXPORT_SYMBOL_GPL(vb2_queue_release); + +/** + * vb2_poll() - implements poll userspace operation + * @q: videobuf2 queue + * @file: file argument passed to the poll file operation handler + * @wait: wait argument passed to the poll file operation handler + * + * This function implements poll file operation handler for a driver. + * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will + * be informed that the file descriptor of a video device is available for + * reading. + * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor + * will be reported as available for writing. + * + * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any + * pending events. + * + * The return values from this function are intended to be directly returned + * from poll handler in driver. + */ +unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) +{ + struct video_device *vfd = video_devdata(file); + unsigned long req_events = poll_requested_events(wait); + struct vb2_buffer *vb = NULL; + unsigned int res = 0; + unsigned long flags; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->wait, wait); + } + + if (!q->is_output && !(req_events & (POLLIN | POLLRDNORM))) + return res; + if (q->is_output && !(req_events & (POLLOUT | POLLWRNORM))) + return res; + + /* + * Start file I/O emulator only if streaming API has not been used yet. + */ + if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { + if (!q->is_output && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) { + if (__vb2_init_fileio(q, 1)) + return res | POLLERR; + } + if (q->is_output && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) { + if (__vb2_init_fileio(q, 0)) + return res | POLLERR; + /* + * Write to OUTPUT queue can be done immediately. + */ + return res | POLLOUT | POLLWRNORM; + } + } + + /* + * There is nothing to wait for if the queue isn't streaming, or if the + * error flag is set. + */ + if (!vb2_is_streaming(q) || q->error) + return res | POLLERR; + /* + * For compatibility with vb1: if QBUF hasn't been called yet, then + * return POLLERR as well. This only affects capture queues, output + * queues will always initialize waiting_for_buffers to false. + */ + if (q->waiting_for_buffers) + return res | POLLERR; + + /* + * For output streams you can call write() as long as there are fewer + * buffers queued than there are buffers available. + */ + if (q->is_output && q->fileio && q->queued_count < q->num_buffers) + return res | POLLOUT | POLLWRNORM; + + if (list_empty(&q->done_list)) { + /* + * If the last buffer was dequeued from a capture queue, + * return immediately. DQBUF will return -EPIPE. + */ + if (q->last_buffer_dequeued) + return res | POLLIN | POLLRDNORM; + + poll_wait(file, &q->done_wq, wait); + } + + /* + * Take first buffer available for dequeuing. + */ + spin_lock_irqsave(&q->done_lock, flags); + if (!list_empty(&q->done_list)) + vb = list_first_entry(&q->done_list, struct vb2_buffer, + done_entry); + spin_unlock_irqrestore(&q->done_lock, flags); + + if (vb && (vb->state == VB2_BUF_STATE_DONE + || vb->state == VB2_BUF_STATE_ERROR)) { + return (q->is_output) ? + res | POLLOUT | POLLWRNORM : + res | POLLIN | POLLRDNORM; + } + return res; +} +EXPORT_SYMBOL_GPL(vb2_poll); + +/** + * struct vb2_fileio_buf - buffer context used by file io emulator + * + * vb2 provides a compatibility layer and emulator of file io (read and + * write) calls on top of streaming API. This structure is used for + * tracking context related to the buffers. + */ +struct vb2_fileio_buf { + void *vaddr; + unsigned int size; + unsigned int pos; + unsigned int queued:1; +}; + +/** + * struct vb2_fileio_data - queue context used by file io emulator + * + * @cur_index: the index of the buffer currently being read from or + * written to. If equal to q->num_buffers then a new buffer + * must be dequeued. + * @initial_index: in the read() case all buffers are queued up immediately + * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles + * buffers. However, in the write() case no buffers are initially + * queued, instead whenever a buffer is full it is queued up by + * __vb2_perform_fileio(). Only once all available buffers have + * been queued up will __vb2_perform_fileio() start to dequeue + * buffers. This means that initially __vb2_perform_fileio() + * needs to know what buffer index to use when it is queuing up + * the buffers for the first time. That initial index is stored + * in this field. Once it is equal to q->num_buffers all + * available buffers have been queued and __vb2_perform_fileio() + * should start the normal dequeue/queue cycle. + * + * vb2 provides a compatibility layer and emulator of file io (read and + * write) calls on top of streaming API. For proper operation it required + * this structure to save the driver state between each call of the read + * or write function. + */ +struct vb2_fileio_data { + struct v4l2_requestbuffers req; + struct v4l2_plane p; + struct v4l2_buffer b; + struct vb2_fileio_buf bufs[VB2_MAX_FRAME]; + unsigned int cur_index; + unsigned int initial_index; + unsigned int q_count; + unsigned int dq_count; + unsigned read_once:1; + unsigned write_immediately:1; +}; + +/** + * __vb2_init_fileio() - initialize file io emulator + * @q: videobuf2 queue + * @read: mode selector (1 means read, 0 means write) + */ +static int __vb2_init_fileio(struct vb2_queue *q, int read) +{ + struct vb2_fileio_data *fileio; + int i, ret; + unsigned int count = 0; + + /* + * Sanity check + */ + if (WARN_ON((read && !(q->io_modes & VB2_READ)) || + (!read && !(q->io_modes & VB2_WRITE)))) + return -EINVAL; + + /* + * Check if device supports mapping buffers to kernel virtual space. + */ + if (!q->mem_ops->vaddr) + return -EBUSY; + + /* + * Check if streaming api has not been already activated. + */ + if (q->streaming || q->num_buffers > 0) + return -EBUSY; + + /* + * Start with count 1, driver can increase it in queue_setup() + */ + count = 1; + + dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", + (read) ? "read" : "write", count, q->fileio_read_once, + q->fileio_write_immediately); + + fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL); + if (fileio == NULL) + return -ENOMEM; + + fileio->read_once = q->fileio_read_once; + fileio->write_immediately = q->fileio_write_immediately; + + /* + * Request buffers and use MMAP type to force driver + * to allocate buffers by itself. + */ + fileio->req.count = count; + fileio->req.memory = VB2_MEMORY_MMAP; + fileio->req.type = q->type; + q->fileio = fileio; + ret = vb2_core_reqbufs(q, fileio->req.memory, &fileio->req.count); + if (ret) + goto err_kfree; + + /* + * Check if plane_count is correct + * (multiplane buffers are not supported). + */ + if (q->bufs[0]->num_planes != 1) { + ret = -EBUSY; + goto err_reqbufs; + } + + /* + * Get kernel address of each buffer. + */ + for (i = 0; i < q->num_buffers; i++) { + fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0); + if (fileio->bufs[i].vaddr == NULL) { + ret = -EINVAL; + goto err_reqbufs; + } + fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0); + } + + /* + * Read mode requires pre queuing of all buffers. + */ + if (read) { + bool is_multiplanar = q->is_multiplanar; + + /* + * Queue all buffers. + */ + for (i = 0; i < q->num_buffers; i++) { + struct v4l2_buffer *b = &fileio->b; + + memset(b, 0, sizeof(*b)); + b->type = q->type; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + b->m.planes = &fileio->p; + b->length = 1; + } + b->memory = q->memory; + b->index = i; + ret = vb2_internal_qbuf(q, b); + if (ret) + goto err_reqbufs; + fileio->bufs[i].queued = 1; + } + /* + * All buffers have been queued, so mark that by setting + * initial_index to q->num_buffers + */ + fileio->initial_index = q->num_buffers; + fileio->cur_index = q->num_buffers; + } + + /* + * Start streaming. + */ + ret = vb2_core_streamon(q, q->type); + if (ret) + goto err_reqbufs; + + return ret; + +err_reqbufs: + fileio->req.count = 0; + vb2_core_reqbufs(q, fileio->req.memory, &fileio->req.count); + +err_kfree: + q->fileio = NULL; + kfree(fileio); + return ret; +} + +/** + * __vb2_cleanup_fileio() - free resourced used by file io emulator + * @q: videobuf2 queue + */ +static int __vb2_cleanup_fileio(struct vb2_queue *q) +{ + struct vb2_fileio_data *fileio = q->fileio; + + if (fileio) { + vb2_core_streamoff(q, q->type); + q->fileio = NULL; + fileio->req.count = 0; + vb2_reqbufs(q, &fileio->req); + kfree(fileio); + dprintk(3, "file io emulator closed\n"); + } + return 0; +} + +/** + * __vb2_perform_fileio() - perform a single file io (read or write) operation + * @q: videobuf2 queue + * @data: pointed to target userspace buffer + * @count: number of bytes to read or write + * @ppos: file handle position tracking pointer + * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) + * @read: access mode selector (1 means read, 0 means write) + */ +static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) +{ + struct vb2_fileio_data *fileio; + struct vb2_fileio_buf *buf; + bool is_multiplanar = q->is_multiplanar; + /* + * When using write() to write data to an output video node the vb2 core + * should set timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody + * else is able to provide this information with the write() operation. + */ + bool set_timestamp = !read && + (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY; + int ret, index; + + dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", + read ? "read" : "write", (long)*ppos, count, + nonblock ? "non" : ""); + + if (!data) + return -EINVAL; + + /* + * Initialize emulator on first call. + */ + if (!vb2_fileio_is_active(q)) { + ret = __vb2_init_fileio(q, read); + dprintk(3, "vb2_init_fileio result: %d\n", ret); + if (ret) + return ret; + } + fileio = q->fileio; + + /* + * Check if we need to dequeue the buffer. + */ + index = fileio->cur_index; + if (index >= q->num_buffers) { + /* + * Call vb2_dqbuf to get buffer back. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + fileio->b.m.planes = &fileio->p; + fileio->b.length = 1; + } + ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); + dprintk(5, "vb2_dqbuf result: %d\n", ret); + if (ret) + return ret; + fileio->dq_count += 1; + + fileio->cur_index = index = fileio->b.index; + buf = &fileio->bufs[index]; + + /* + * Get number of bytes filled by the driver + */ + buf->pos = 0; + buf->queued = 0; + buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0) + : vb2_plane_size(q->bufs[index], 0); + /* Compensate for data_offset on read in the multiplanar case. */ + if (is_multiplanar && read && + fileio->b.m.planes[0].data_offset < buf->size) { + buf->pos = fileio->b.m.planes[0].data_offset; + buf->size -= buf->pos; + } + } else { + buf = &fileio->bufs[index]; + } + + /* + * Limit count on last few bytes of the buffer. + */ + if (buf->pos + count > buf->size) { + count = buf->size - buf->pos; + dprintk(5, "reducing read count: %zd\n", count); + } + + /* + * Transfer data to userspace. + */ + dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", + count, index, buf->pos); + if (read) + ret = copy_to_user(data, buf->vaddr + buf->pos, count); + else + ret = copy_from_user(buf->vaddr + buf->pos, data, count); + if (ret) { + dprintk(3, "error copying data\n"); + return -EFAULT; + } + + /* + * Update counters. + */ + buf->pos += count; + *ppos += count; + + /* + * Queue next buffer if required. + */ + if (buf->pos == buf->size || (!read && fileio->write_immediately)) { + /* + * Check if this is the last buffer to read. + */ + if (read && fileio->read_once && fileio->dq_count == 1) { + dprintk(3, "read limit reached\n"); + return __vb2_cleanup_fileio(q); + } + + /* + * Call vb2_qbuf and give buffer to the driver. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + fileio->b.index = index; + fileio->b.bytesused = buf->pos; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + fileio->p.bytesused = buf->pos; + fileio->b.m.planes = &fileio->p; + fileio->b.length = 1; + } + if (set_timestamp) + v4l2_get_timestamp(&fileio->b.timestamp); + ret = vb2_internal_qbuf(q, &fileio->b); + dprintk(5, "vb2_dbuf result: %d\n", ret); + if (ret) + return ret; + + /* + * Buffer has been queued, update the status + */ + buf->pos = 0; + buf->queued = 1; + buf->size = vb2_plane_size(q->bufs[index], 0); + fileio->q_count += 1; + /* + * If we are queuing up buffers for the first time, then + * increase initial_index by one. + */ + if (fileio->initial_index < q->num_buffers) + fileio->initial_index++; + /* + * The next buffer to use is either a buffer that's going to be + * queued for the first time (initial_index < q->num_buffers) + * or it is equal to q->num_buffers, meaning that the next + * time we need to dequeue a buffer since we've now queued up + * all the 'first time' buffers. + */ + fileio->cur_index = fileio->initial_index; + } + + /* + * Return proper number of bytes processed. + */ + if (ret == 0) + ret = count; + return ret; +} + +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) +{ + return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); +} +EXPORT_SYMBOL_GPL(vb2_read); + +size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblocking) +{ + return __vb2_perform_fileio(q, (char __user *) data, count, + ppos, nonblocking, 0); +} +EXPORT_SYMBOL_GPL(vb2_write); + +struct vb2_threadio_data { + struct task_struct *thread; + vb2_thread_fnc fnc; + void *priv; + bool stop; +}; + +static int vb2_thread(void *data) +{ + struct vb2_queue *q = data; + struct vb2_threadio_data *threadio = q->threadio; + struct vb2_fileio_data *fileio = q->fileio; + bool set_timestamp = false; + int prequeue = 0; + int index = 0; + int ret = 0; + + if (q->is_output) { + prequeue = q->num_buffers; + set_timestamp = + (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY; + } + + set_freezable(); + + for (;;) { + struct vb2_buffer *vb; + + /* + * Call vb2_dqbuf to get buffer back. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + if (prequeue) { + fileio->b.index = index++; + prequeue--; + } else { + call_void_qop(q, wait_finish, q); + if (!threadio->stop) + ret = vb2_internal_dqbuf(q, &fileio->b, 0); + call_void_qop(q, wait_prepare, q); + dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + } + if (ret || threadio->stop) + break; + try_to_freeze(); + + vb = q->bufs[fileio->b.index]; + if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR)) + if (threadio->fnc(vb, threadio->priv)) + break; + call_void_qop(q, wait_finish, q); + if (set_timestamp) + v4l2_get_timestamp(&fileio->b.timestamp); + if (!threadio->stop) + ret = vb2_internal_qbuf(q, &fileio->b); + call_void_qop(q, wait_prepare, q); + if (ret || threadio->stop) + break; + } + + /* Hmm, linux becomes *very* unhappy without this ... */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return 0; +} + +/* + * This function should not be used for anything else but the videobuf2-dvb + * support. If you think you have another good use-case for this, then please + * contact the linux-media mailinglist first. + */ +int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, + const char *thread_name) +{ + struct vb2_threadio_data *threadio; + int ret = 0; + + if (q->threadio) + return -EBUSY; + if (vb2_is_busy(q)) + return -EBUSY; + if (WARN_ON(q->fileio)) + return -EBUSY; + + threadio = kzalloc(sizeof(*threadio), GFP_KERNEL); + if (threadio == NULL) + return -ENOMEM; + threadio->fnc = fnc; + threadio->priv = priv; + + ret = __vb2_init_fileio(q, !q->is_output); + dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + if (ret) + goto nomem; + q->threadio = threadio; + threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name); + if (IS_ERR(threadio->thread)) { + ret = PTR_ERR(threadio->thread); + threadio->thread = NULL; + goto nothread; + } + return 0; + +nothread: + __vb2_cleanup_fileio(q); +nomem: + kfree(threadio); + return ret; +} +EXPORT_SYMBOL_GPL(vb2_thread_start); + +int vb2_thread_stop(struct vb2_queue *q) +{ + struct vb2_threadio_data *threadio = q->threadio; + int err; + + if (threadio == NULL) + return 0; + threadio->stop = true; + /* Wake up all pending sleeps in the thread */ + vb2_queue_error(q); + err = kthread_stop(threadio->thread); + __vb2_cleanup_fileio(q); + threadio->thread = NULL; + kfree(threadio); + q->threadio = NULL; + return err; +} +EXPORT_SYMBOL_GPL(vb2_thread_stop); + +/* + * The following functions are not part of the vb2 core API, but are helper + * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations + * and struct vb2_ops. + * They contain boilerplate code that most if not all drivers have to do + * and so they simplify the driver code. + */ + +/* The queue is busy if there is a owner and you are not that owner. */ +static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) +{ + return vdev->queue->owner && vdev->queue->owner != file->private_data; +} + +/* vb2 ioctl helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); + + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have a new owner. */ + if (res == 0) + vdev->queue->owner = p->count ? file->private_data : NULL; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); + +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = vb2_verify_memory_type(vdev->queue, p->memory, + p->format.type); + + p->index = vdev->queue->num_buffers; + /* + * If count == 0, then just check if memory and type are valid. + * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. + */ + if (p->count == 0) + return res != -EBUSY ? res : 0; + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = vb2_core_create_bufs(vdev->queue, p->memory, &p->count, + &p->format); + if (res == 0) + vdev->queue->owner = file->private_data; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); + +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_prepare_buf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); + +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ + return vb2_querybuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); + +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_qbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); + +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); + +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamon(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); + +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamoff(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); + +int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_expbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); + +/* v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_mmap(vdev->queue, vma); +} +EXPORT_SYMBOL_GPL(vb2_fop_mmap); + +int _vb2_fop_release(struct file *file, struct mutex *lock) +{ + struct video_device *vdev = video_devdata(file); + + if (lock) + mutex_lock(lock); + if (file->private_data == vdev->queue->owner) { + vb2_queue_release(vdev->queue); + vdev->queue->owner = NULL; + } + if (lock) + mutex_unlock(lock); + return v4l2_fh_release(file); +} +EXPORT_SYMBOL_GPL(_vb2_fop_release); + +int vb2_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + + return _vb2_fop_release(file, lock); +} +EXPORT_SYMBOL_GPL(vb2_fop_release); + +ssize_t vb2_fop_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int err = -EBUSY; + + if (!(vdev->queue->io_modes & VB2_WRITE)) + return -EINVAL; + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_write(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (vdev->queue->fileio) + vdev->queue->owner = file->private_data; +exit: + if (lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_write); + +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int err = -EBUSY; + + if (!(vdev->queue->io_modes & VB2_READ)) + return -EINVAL; + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_read(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (vdev->queue->fileio) + vdev->queue->owner = file->private_data; +exit: + if (lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_read); + +unsigned int vb2_fop_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct vb2_queue *q = vdev->queue; + struct mutex *lock = q->lock ? q->lock : vdev->lock; + unsigned res; + void *fileio; + + /* + * If this helper doesn't know how to lock, then you shouldn't be using + * it but you should write your own. + */ + WARN_ON(!lock); + + if (lock && mutex_lock_interruptible(lock)) + return POLLERR; + + fileio = q->fileio; + + res = vb2_poll(vdev->queue, file, wait); + + /* If fileio was started, then we have a new queue owner. */ + if (!fileio && q->fileio) + q->owner = file->private_data; + if (lock) + mutex_unlock(lock); + return res; +} +EXPORT_SYMBOL_GPL(vb2_fop_poll); + +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); +} +EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); +#endif + +/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq) +{ + mutex_unlock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); + +void vb2_ops_wait_finish(struct vb2_queue *vq) +{ + mutex_lock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); + +MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); +MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/media/v4l2-core/videobuf2-vmalloc.c b/kernel/drivers/media/v4l2-core/videobuf2-vmalloc.c index 657ab302a..1c302743a 100644 --- a/kernel/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/kernel/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -17,17 +17,15 @@ #include #include -#include +#include #include #include struct vb2_vmalloc_buf { void *vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct frame_vector *vec; enum dma_data_direction dma_dir; unsigned long size; - unsigned int n_pages; atomic_t refcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct frame_vector *vec; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,51 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto fail_pfnvec_create; + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* + * We cannot get page pointers for these pfns. Check memory is + * physically contiguous and use direct mapping. + */ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, buf->n_pages, - dma_dir == DMA_FROM_DEVICE, - 1, /* force */ - buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, - buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: +fail_map: + vb2_destroy_framevec(vec); +fail_pfnvec_create: kfree(buf); return NULL; @@ -143,20 +124,21 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) struct vb2_vmalloc_buf *buf = buf_priv; unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned int i; + struct page **pages; + unsigned int n_pages; - if (buf->pages) { + if (!buf->vec->is_pfns) { + n_pages = frame_vector_count(buf->vec); + pages = frame_vector_pages(buf->vec); if (vaddr) - vm_unmap_ram((void *)vaddr, buf->n_pages); - for (i = 0; i < buf->n_pages; ++i) { - if (buf->dma_dir == DMA_FROM_DEVICE) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); - } - kfree(buf->pages); + vm_unmap_ram((void *)vaddr, n_pages); + if (buf->dma_dir == DMA_FROM_DEVICE) + for (i = 0; i < n_pages; i++) + set_page_dirty_lock(pages[i]); } else { - vb2_put_vma(buf->vma); iounmap((__force void __iomem *)buf->vaddr); } + vb2_destroy_framevec(buf->vec); kfree(buf); } @@ -287,7 +269,6 @@ static struct sg_table *vb2_vmalloc_dmabuf_ops_map( /* stealing dmabuf mutex to serialize map/unmap operations */ struct mutex *lock = &db_attach->dmabuf->lock; struct sg_table *sgt; - int ret; mutex_lock(lock); @@ -306,8 +287,9 @@ static struct sg_table *vb2_vmalloc_dmabuf_ops_map( } /* mapping to the client with new direction */ - ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir); - if (ret <= 0) { + sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, + dma_dir); + if (!sgt->nents) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); -- cgit 1.2.3-korg